微信终端跨平台组件 Mars 系列(三)连接超时与IPPort排序
前言
Mars 是微信官方的终端基础组件,是一个使用 C++ 编写的业务无关、跨平台的基础组件。目前在微信 Android、iOS、Windows、Mac、WP 等多个平台中使用。Mars 主要包括以下几个独立的部分:
COMM:基础库,包括socket、线程、消息队列、协程等基础工具;
XLOG:通用日志模块,充分考虑移动终端的特点,提供高性能、高可用、安全性、容错性的日志功能;(详情点击:高性能日志模块xlog)
SDT:网络诊断模块;
STN:信令传输网络模块,负责终端与服务器的小数据信令通道。包含了微信终端在移动网络上的大量优化经验与成果,经历了微信海量用户的考验。
Mars 系列开始,将为大家介绍 STN(信令传输网络模块)。由于 STN 的复杂性,该模块将被分解为多个篇章进行介绍。本文主要介绍微信中关于 socket 连接及 IP&Port 选择的思考与设计。
你需要知道的TCP连接
TCP 协议应该是目前使用的最广泛的传输层协议,它提供了可靠的端到端的传输,为应用的设计节省了大量的工作。TCP 建立连接的”三次握手”与连接终止的“四次挥手”也广为人知。在这简单的 connect 调用中,还能做怎样的思考与设计呢?
int connect(int sockfd, const struct *addr, socklen_t addrlen)
连接的超时重传
超时与重传是 TCP 协议最核心的部分,在不稳定的移动网络中,超时重传的设计尤为重要。在连接建立的过程中,由于网络本身的不可靠特性,不可避免的需要重传的机制来保障可靠服务。在《TCP/IP详解 卷1》的描述中,在大多数 BSD 实现中,若主动 connect 方没有收到 SYN 的回应,会在第6秒发送第2个 SYN 进行重试,第3个 SYN 则是与第2个间隔24秒。在第75秒还没有收到回应,则 connect 调用返回 ETIMEOUT。
这就意味着,在不能立刻确认失败(例如 unreachable 等)的情况下,需要75秒的时间,才能获得结果。如果真相并不是用户的网络不可用,而是某台服务器故障、繁忙、网络不稳定等因素,那75秒的时间只能尝试1个 IP&Port 资源,对于大多数移动应用而言,是不可接受的。我们需要更积极的超时重传机制!!!
然而,我们并不能修改 TCP 的协议栈,我们只能在应用层进行干预,设计应用层的超时机制。说干就干,这个时候你是否已经在构思新的、应用层的连接超时重传机制了呢?应用层的超时重传,典型做法就是提前结束 connect 的阻塞调用,使用新的 IP&Port 资源进行 connect 重试。但是,我们应该选择怎样的连接超时值呢?4秒?10秒?20秒?30秒?不同的应用场景会有不同的选择。我们来看一下常见的几种场景:
连不同 or 网络不可用等
服务器繁忙 or 中间路由故障等
基站繁忙 or 连接信号弱 or 丢包率高等
在第一种场景中,连接超时设置不会带来什么区别。在第二种场景中,部分服务器资源或路由不可用,我们希望连接超时能稍微短一些,使得我们能尽快的发现故障,并且通过更换 IP&Port 的方式获得可用资源或路由路径。而第三种场景则是在移动网络中经常遇到的弱网络的场景。在这种场景中,我们更换 IP&Port 资源也是无效的,因此希望连接超时能相对长一些,进行更多的TCP层的重传。(当然,也不是超时越长越好,后面的分析可以看到很多等待时长是效果低微的)
不同的场景对连接超时有不同的需求,然而,我们在程序中并没有很好的方法来区分这些场景。在进行连接超时这个阈值的选择前,我们先来看看,当前主流的 android、iOS 操作系统的连接设计。android 的 TCP 层连接超时重传如下图所示(测试机型为 nexus5,android 4.4)。超时间隔依次为(1,2,4,8,16),第5次重试后32秒返回 ETIMEOUT,总用时63秒。超时设置符合 Linux 的常规设置。
但在不同的机型中,偶尔会出现差异性。如下图 android 抓包(三星 android 4.4)。
iOS 的 connect 超时重传如下图所示。超时间隔依次为(1,1,1,1,1,2,4,8,16,32),总共是67s。
经过 tcpdump 的调研分析后,我们发现:
在 iOS 系统中对 connect 的超时重传进行了一定的修改,在 connect 初期使用更积极的策略,以适应移动网络的不稳定特征。而在 android 系统中,connect 超时重传则使用了较为“懒惰”、适用于有线网络的超时重传间隔;
不管什么平台,连接总超时时长都需要1分钟左右,这个时长在大多数移动应用中,都是不符合用户体验要求的;
连接的初始阶段,TCP 超时重传会更积极一些,越到后面,重传间隔越大。
因此,在实际的连接超时设置上,我们根据不同的系统特征,结合应用能接受的“用户体验”范围,可以设置不同的连接超时间隔。例如在 iOS 系统中,由于采用了较为积极的超时间隔,我们可以将 connect 调用的超时设置为10s。在10s内,iOS 会自动进行6次的重发。在 android 系统中,系统会在第7秒发起第3次重发,之后需要在第15秒才会重发。在不同的用户体验要求下,应用可以将 connect 的调用超时设置为不同的值。例如也可以设置为10s(意味着给第3次重发3s的等待时间),从而避免无效的等待时长。同时通过更换 IP&Port 后,重新调用 connect 操作的方式,来获得更积极的重发策略,更快的查找到可用的 IP&Port 组合。
连接的终止
“四次挥手”的连接终止协议已经口熟能详。过程如下图所示。需要关注的是,图中主动关闭的一方会进入 TIME_WAIT 状态,在此状态中通常将停留2倍的 MSL 时长。MSL 时长在不同的操作系统中有不同的设置,通常在30秒到60秒。TIME_WAIT 的数量太多会导致耗尽主动关闭方的 socket 端口和句柄,导致无法再发起新的连接,进而严重影响主动关闭方的并发性能。虽然在实际的使用中,可以通过 tcp_tw_recycle,tcp_tw_reuse,tcp_max_tw_buckets 等方式缓解该问题,但也会带来一些副作用。最好的解决方案是在协议的设计上,尽量的由终端来发起关闭的操作,避免服务器的大量 TIME_WAIT 状态。例如,使用长连接避免频繁的关闭;在短连接的协议设计上,务必加上终止标记(例如 http 头部加上 content-length )使得可以由终端来发起关闭的操作。
串行连接 VS 并发连接 VS 复合连接
在上述的连接超时策略中,我们选择10秒的连接超时。这就意味着我们需要10秒的时间来确认一个 IP&Port 组合的 connect 超时。当我们有多个 IP&Port 资源时,遍历的效率偏低。那我们是否能设置 connect 的超时为更短呢?例如4秒。我们知道移动互联网具有不稳定的特征,超时时间设置过短,会导致在弱网络的情况下,connect 总是失败,导致不可用。串行连接的策略在超时选择上,由于需要兼顾高性能与高可用的设计目标,使得该策略是一个相对“慢”的连接策略。
与此相应,我们会想到并发连接的策略。并发连接,同时发起对N个 IP&Port 的连接调用,可以让我们第一时间发现可用的连接,并且还顺带发现了 connect 最快的 IP&Port 配置。并发连接可以一举解决了“高性能”、“高可用”的设计目标,看起来很完美。然而,这个时候,服务端的同学“跳”起来了。在并发连接的策略下,服务器需要提供的连接能力是串行连接的N倍,对服务器连接资源是极大的浪费。同时,并发连接是否会引起连接资源的竞争,从而影响网络正常用户的常规体验,也是个未知的因素。
让我们来回顾串行连接与并行连接的优缺点。
串行连接
资源占用少
无服务器负载问题
超时选择困难
最慢可用
并行连接
网络资源竞争
服务器负载高
最快可用
那么,有没有一种策略,能同时满足高性能、高可用、低负载的目标呢?在微信的连接设计中,我们使用了”复合连接“的策略。如下图所示。
初始阶段,应用发起对 IP1 &Port1 的 connect 调用。在第4秒的时候,如果第一个 connect 还没有返回,则发起对 IP2 &Port2 的 connect 调用。以此类推,直至发起了5组 IP&Port 的 connect 调用。
对比串行连接与并行连接,复合连接有以下特点:
常规情况下,服务器负载与串行连接策略相同,实现了低负载的目标;
异常情况下,每4s发起新(IP,Port)组合的 connect 调用,使得应用可以快速的查找可用 IP&Port,实现高性能的目标;
在超时时间的选择上,复合方式的“并发”已经实现了高性能、低负载的目标,因此在超时时间的选择上可以相对宽松,以保障高可用为重。
综合对比,复合连接能够维持低资源消耗的情况下,能同时实现低负载、高性能、高可用的目标。
微信 IP&Port 排序算法的演进
在建立连接的调用中,除了超时时间的设置外,IP&Port是连接的最重要参数。IP&Port 的排序、选择对于 connect 的性能也是有着重大的影响。本节主要讨论在已知 IP 列表、Port 列表的情况下,如何排序、组合的问题,而不讨论如何获得就近接入等问题。
IP&Port 的组成
在微信中,IP有多种来源类型。优先级从上而下分别为:
WXDNS IP
DNS IP
Auth IP
Hardcode IP
WXDNS IP 是通过微信自建的 DNS 服务获得的IP列表,自建 DNS 对防劫持、有效期控制等有重要作用。DNS IP 则是通过常规的 DNS 解析获得的 IP 列表。Auth IP 是微信动态下发的保底IP列表。而Hardcode IP 则是最终的保底IP列表。总体而言,分为常规IP列表、保底IP列表两个类别。WXDNS IP、DNS IP 为常规列表,Auth IP,Hardcode IP 为保底列表。同时,在组成实际使用的 IP&Port 列表时,由于 WXDNS 与 DNS 的功能近似,因此通常只出现其中一种类型的IP列表。Auth IP 与Hardcode IP 的功能近似,也是同时只能出现两者中的一种类型。
在 Port 的选择上,微信服务在常规情况下提供2个端口,预防端口被封锁的情况。特别情况下,可以通过配置下发进行端口更新。
IP&Port排序算法(一):随机组合排序算法
每个TCP连接都是以 IP&Port 的组合为唯一标识。在 IP&Port 的选择上,我们初步归纳为2个目标:
高可用:尽快的找到可用的 IP&Port 资源
高性能:优先使用质量好的 IP&Port
负载均衡:IP的排序算法不带任何偏向因子,避免造成人为的负载不均衡
在微信早期的排序选择上,我们使用了一种随机组合的排序算法。即将 WXDNS or DNS IP 列表与 Port 列表进行组合,组合后的结果进行随机排序。在随机排序的结果列表中,使用下述步骤进行排序:
选取IP1+Port1;
选取IP2+Port2,尽量使得IP1与IP2不相等,Port1与Port2不相等;
选取IP3+Port3,尽量使得IP3与IP1、IP2都不相等,Port3与Port1、Port2都不相等;
以此类推,形成常规列表。
同理,使用 Auth IP or Hardcode IP 列表与 Port 列表的组合,我们按照相同算法生成另外一份保底列表,并将保底列表排序在常规列表的后面,从而组成完整的 IP&Port 列表。随机组合排序的算法有着以下的特点:
高性能:每一次尝试都尽量使用完全不同的资源,使得能最快的发现可用资源;
初始随机,从而避免列表顺序的固化;
保底列表在最后,形成最后的保护屏障;
在不同的网络下,维护着不同的资源列表。
在使用中,如果发现 IP&Port 访问失败,则在列表中 ban 掉该资源。这里有个小优化,即当 IP1&Port1 的上一次访问成功时,需要连续失败2次才 ban 该资源。目的是为了减小偶然的网络抖动造成的影响。
随机组合排序算法的设计初衷,是为了以最快的速度尝试不同的资源组合,从而快速寻找到可用的资源。然而,在微信的实际使用中,却发现这种算法存在着诸多的问题。例如:
网络不可用或网络较大波动情况下,列表被ban的速度较快;
Auth IP or Hardcode IP 列表太容易被访问到:随着常规资源陆续被ban,保底资源总是会被访问到,造成对保底资源的访问量大。保底资源是为了微信服务这不符合保底资源的设计初衷。
当引入复合连接策略后,IP资源不足。这是因为 ban 的策略简单粗暴的丢弃失败的 IP,导致 IP 资源越来越少;
每次缓存超时或列表轮空后,对于新列表没有经验信息可用
在随机组合排序算法的基础上,为了解决遇到的新问题,微信使用了新的“以史为鉴”的算法。
IP&Port 排序算法(二):以史为鉴
由于复合连接的引入,在每次复合连接的尝试中,微信可以伪“并发”的对N个 IP&Port 进行 connect(微信中目前N=5)。简单的ban丢弃的策略会使得 IP 资源越来越少。 针对这个特点,我们对IP&Port算法进行了以下修改:
初始资源列表分为两类列表:常规列表,保底列表,分别使用方案(一)随机组合排序算法生成初始顺序;
对每次复合连接使用的列表,规定5个资源的组成是4个常规资源+1个保底资源,并且保底资源在最后(完全无法获取常规资源的情况除外)。这种资源组成方式一方面解决了“保底资源太容易被访问到”的问题,一方面也保障了保底资源的作用;
在不同网络中,分别记录每个 IP&Port 的使用情况,并根据使用记录进行评分、排序;
区分连续记录:对每个 IP&Port 的更新,10秒内的连续成功或失败,不进行使用情况的记录。这种处理方式一方面是为了避免网络不可用或网络出现较大波动时,IP资源被过快的错误标记;一方面也避免失败历史被快速的覆盖;
最近的8条使用记录中,如果有超过3条失败记录,且最新一次失败记录时间为10分钟内,则本次排序ban该记录。这种处理方式的目的是避免历史分数较高的 IP&Port 在突然出现故障时很难被排序算法排除的问题;
无历史的记录使用随机评分排序。
通过上述方法,我们保证了保底资源不会被轻易访问到,解决了列表被快速标记的问题,同时也保证了历史记录好的资源在出现故障时也能被快速替换。
IP&Port 排序算法(三):遗忘历史
“以史为鉴”的方案在微信中使用了一段时间,看起来运行良好。直至某一天,微信的部分服务集群出现了故障。虽然微信客户端快速的切换到可用的服务器资源,但当故障服务器恢复后,微信客户端却迟迟没有分流到已恢复服务的集群,导致部分微信服务器负载过高,而部分微信服务器却负载较低的情况。通过分析,发现“以史为鉴”的排序方案存在着一些问题:
初始阶段排在前面的资源容易获得较多的成功记录,从而分数始终维持在较高的水平;
出灾情况下,故障机器由于有失败记录,使得很难获得“被原谅”的机会,从而也很难更新使用历史;
采用了无历史记录随机评分,破坏了原有的“相邻记录尽量不相同”的随机性设计;
因此,好的 IP&Port 排序算法,不仅应该快速的发现可用的资源,使得在出灾情况下能快速的响应,同时,也应该具备一定的“遗忘性”、“容灾性”,使得灾情恢复后能较快的发现“灾情恢复”这一事实,并且进行重排序,使得服务器资源得到更合理的使用。在综合考虑“以史为鉴”和“遗忘历史”后,新的 方案具有以下特征:
内存历史、文件历史双层记录历史:反映资源使用的近期情况及历史情况;
初始化状态:每次进程重启或网络切换后,从文件历史中“压缩”出内存历史作为初始状态;
旁路检测:额外更新历史的渠道,更有助于挑选高性能的资源,并且帮助“灾情恢复”的资源获得使用的机会;
文件历史的遗忘性:文件历史每24小时强制刷新,避免高分数的记录长期“占有”队列;
无历史、有历史的混合排序。
具体实现查看 Mars 源代码中的 simple_ipport_sort。
总结
连接是信令传输的前提,一个简单的连接操作蕴含着不少的优化空间。在连接超时的选择上,我们要兼顾性能与可用性,过短的连接超时可能导致弱网络下的低可用性,但过长的连接超时又影响用户体验。在 STN 中,我们结合系统本身的 TCP 连接重传特性,进行了相应的设计考量。即使如此,串行的连接方案仍然不能满足高性能的需求。并发连接的方案获得高性能的同时,也带来了服务器负载剧增的损失。综合考虑下,STN 使用了“复合连接”的方案,获得高性能的同时,也保证通常情况下的服务器低负载。
IP&Port 是连接的最重要资源,IP&Port 的排序选择是连接过程的重要部分。在微信的实际使用中,我们依次使用了“随机组合”、“以史为鉴”、“遗忘历史”三种方案,综合的考虑了查找性能、移动互联网的不稳定性、容灾及容灾恢复等。
连接超时、连接策略及 IP&Port 排序是连接的是三个重要组成部分,相关的方案也随着微信实践在不断的发展中。相信在不同的应用场景中,我们可能会遇到更多的不同问题及需求。随着Mars的开源,也能有机会参考、吸收其他应用中的实战经验,使得网络优化持续的深入。
关注 Mars , 来 Github 给我们 star 吧
https://github.com/Tencent/mars
查看 Mars 项目源码,请点击[阅读原文]。
https://mp.weixin.qq.com/s?__biz=MzAwNDY1ODY2OQ==&mid=2649286458&idx=1&sn=320f690faa4f97f7a49a291d4de174a9&chksm=8334c3b8b4434aae904b6d590027b100283ef175938610805dd33ca53f004bd3c56040b11fa6#rd
微信终端跨平台组件 Mars 系列(三)连接超时与IPPort排序相关推荐
- 整理:微信终端跨平台组件 mars 系列(一) - 高性能日志模块xlog
学习网站:微信终端跨平台组件 mars 系列(一) - 高性能日志模块xlog 其中涉及知识: 垃圾回收机制 垃圾回收(garbage collection,简称GC)可以自动清空堆中不再使用的对象. ...
- 微信终端跨平台组件 mars 系列(一) - 高性能日志模块xlog
前言 mars 是微信官方的终端基础组件,是一个使用 C++ 编写的业务性无关,平台性无关的基础组件.目前已接入微信 Android.iOS.Mac.Windows.WP 等客户端.现正在筹备开源中, ...
- 【腾讯Bugly干货分享】微信终端跨平台组件 mars 系列(二) - 信令传输超时设计
本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:http://mp.weixin.qq.com/s/9DJxipJaaBC8yC-buHgnTQ 作者简介: ...
- 微信终端跨平台组件 mars 系列(二) - 信令传输超时设计
前言 mars 是微信官方使用 C++ 编写的业务性无关.平台性无关的终端基础组件,目前在微信 Android.iOS.Windows.Mac.Windows Phone 等多个平台中使用,并正在筹备 ...
- [转]微信终端跨平台组件 mars 系列(二) - 信令传输超时设计
转至 http://mp.weixin.qq.com/s/PnICVDyVuMSyvpvTrdEpSQ 感谢作者,支持原创 前言 mars 是微信官方使用 C++ 编写的业务性无关.平台性无关的终端 ...
- 微信终端跨平台组件 Mars 在移动网络的探索和实践
视频观看地址:http://edu.csdn.net/course/detail/4385 在 IM 方面,弱网络一直是横亘在应用开发者面前的一大问题,微信终端跨平台网络基础组件 Mars 团队基于微 ...
- asp.net 安装element ui_Vue组件库系列三:打造属于自己的 UI 库文档(新版本的方案)...
上一章介绍了element-ui老版本中md文档构建过程,如果大家认真看了,就会发现在.md文档中还是有一些重复的部分,怎样使文档看着更加简洁,操作更加方便,饿了么团队下了很大的力气做了探索,现在我们 ...
- 组件库系列三:编写组件库文档
文章目录 vuepress介绍 创建文档工程 配置运行指令 vuepress浏览器自动更新 下载插件和依赖 npm/yarn link docs文件夹 .vuepress文件夹 可收缩代码块 效果展示 ...
- Python开发微信公众号后台(系列三)
原文链接:点击打开链接 摘要: 之前有主流的小黄鸡机器人,但是没找到官方的网页版.(小黄鸡提供付费 API )尝试使用http://www.niurenqushi.com/app/simsimi/(虽 ...
最新文章
- SpringBoot+flowable快速实现工作流,so easy!
- 035_使用Enumeration遍历Vector元素
- babymips(上) 寒假逆向生涯(14/100)
- ftl保存成html中文是乱码,解决freemarker生成静态页面时乱码问题
- Java查看字节码工具
- python接口自动化(四十一)- 发xml格式参数的post请求(超详解)
- 北京互联网地域歧视链
- 建站之星检测不到mysql_建站之星安装,建站之星安装教程 | 帮助信息-动天数据...
- 过麦--回忆里面的山东
- 超细!在浏览器输入xxxhub 回车之后发生了什么?
- 基于51单片机智能大棚智能花盆浇水浇花灌溉补光散热原理图Proteus仿真
- cobol text文件的入出力
- Python3基础知识
- 【话费充值平台】话费充值平台接口设计
- IntelliJ IDEA 之 配置JDK 的 4种方式
- RAM和ROM是什么?RAM和ROM其实都是内存
- 论异步编程的正确姿势:十个接口的活现在只需要一个接口就能搞定!
- VIM 的 commentary 插件
- Maven Dependencies missing jar 解决方法
- 中点法 matlab,中点法解常微分方程(组)
热门文章
- 关于 mybatis-generator自定义注释生成 使用DefaultCommentGenerator重写来完成
- cas sso单点登录 登录过程和登出过程原理说明
- 单元测试(Unit Test)学习
- SAP BW增量队列深入研究
- 根据工作年限预测工资python代码实现
- Gauss-Newton算法学习
- 【Python】判断列表中是否存在一个数
- 科大星云诗社动态20210118
- 吴恩达《Machine Learning》精炼笔记 5:神经网络
- 图像凸性检测函数convexityDefects在Python2.7下使用opencv3.0的问题