一 引言 在2013年11月中旬时,因为基础组件组人手紧张,Leo安排我和春哥去广州轮岗支援。 刚到广州的时候,Ray让我和春哥对Line和WhatsApp的心跳机制进行分析。我和春哥抓包测试了差不多两个多礼拜,在我们基本上摸清了Line和WhatsApp的心跳机制后,Ray才告诉我们真正的任务——对微信的固定心跳进行优化,并告诉我们这不是一件容易的事情。 于是我和春哥开始构思第一个方案,我们开始想用统计的方法来解决问题,当我们拿着第一个方案和Ray讨论时,发现不能优雅应对Ray的所有提问:
Ray和组件组同事在网络方面有极其丰富的经验,虽然他没有给我们指出明确的方向,但提出的问题帮助我们更快的补齐需要面对的核心问题。这两个问题让我和春哥意识到如果能很好的解决,就可以给出一个比较好的心跳方案。第一个问题我和春哥开始就意识到,第二个问题我们确实在一开始时疏忽了。但直接解决这两个问题确实不容易,这着实让我和春哥迷茫了几天,有两三天在纺园我都没怎么睡着,因为想不到更好的方法。 直到有一天思路发生了一些转变,既然最优解比较复杂,为什么不绕过去,使用有损服务理念找次优解呢。让复杂的事情简单化,好了,想到这里突然有一种拨开云雾的感觉。 二 大体思路
三 拍板方案 当我和春哥想出第二个简单易行的方案后,我们心里就很有底了,去找Ray讨论,Ray听完后一次通过,然后Ray约了Harvey,给Harvey讲完后,Harvey说听起来可以,可以试试。 然后就开始动手,分析竞品加确定方案花了差不多两个月。写心跳的主要代码,只花了一天时间,我记得那天是年会后的一天。回过头来再看这个方案花费的时间还是值得的,后来灰度的统计数据显示,70%用户都可以达到我们的心跳上限。 搞完智能心跳后一段时间在广州没事干,我就跟Ray商量,Ray让我去测试下WebView的性能瓶颈。然后我跟周斯基一起来做这件事,搞完了安卓客户端WebView性能瓶颈测试后,因为怀孕的老婆一个人在深圳,领导就安排我先回深圳了。春哥坚守着把GCM部分完成后才回深圳。 等我们的心跳版本正式发布后,一年前我在公司km上分享了智能心跳方案,吸引不少做push的同事加入了讨论,感觉这方面的交流还是很有必要的。 四 方案目标 设计此方案的主要目标是,在尽量不影响用户收消息及时性的前提下,根据网络类型自适应的找出保活信令TCP连接的尽可能大的心跳间隔,从而达到减少安卓微信因心跳引起的空中信道资源消耗,减少心跳Server的负载,以及减少部分因心跳引起的耗电。 主要方法是参考WhatsApp和Line中有价值的做法,结合影响TCP连接寿命的因素,实现Android微信后台自适应心跳算法。 * 有关WhatsApp、Lin的消息推送策略请参考文章《移动端IM实践:WhatsApp、Line、微信的心跳策略分析》; * 有关GCM的研究结论请参考文章《移动端IM实践:谷歌消息推送服务(GCM)研究(来自微信团队)》。 五 方案需考虑到影响连接寿命的思素 在Android下,不管是GCM,还是微信,都是通过TCP长连接来进行消息收发的,TCP长连接存活,消息收发就及时,所以要对影响TCP连接寿命的因素进行研究。 1、NAT超时 大部分移动无线网络运营商都在链路一段时间没有数据通讯时,会淘汰 NAT 表中的对应项,造成链路中断(NAT超时的更多描述见附录9.1)。NAT超时是影响TCP连接寿命的一个重要因素(尤其是国内),所以客户端自动测算NAT超时时间,来动态调整心跳间隔,是一个重要的优化点。 2、DHCP的租期(lease time) 目前测试发现安卓系统对DHCP的处理有Bug,DHCP租期到了不会主动续约并且会继续使用过期IP,这个问题会造成TCP长连接偶然的断连。(租期问题的具体描述见附录9.2)。 3、网络状态变化 手机网络和WIFI网络切换、网络断开和连上等情况有网络状态的变化,也会使长连接变为无效连接,需要监听响应的网络状态变化事件,重新建立Push长连接。 六 心跳范围选择1、前后台区分处理: 为了保证微信收消息及时性的体验,当微信处于前台活跃状态时,使用固定心跳。微信进入后台(或者前台关屏)时,先用几次最小心跳维持长链接。然后进入后台自适应心跳计算。这样做的目的是尽量选择用户不活跃的时间段,来减少心跳计算可能产生的消息不及时收取影响。 2、后台自适应心跳选择区间: 可根据自身产品的特点选择合适的心跳范围。 以下是状态转换示意图: 七 自适应心跳算法量化描述 因为每个网络的NAT时间可能不一致。所以需要区分计算,数据网络按subType做关键字,WIFI按WIFI名做关键字。对稳定的网络,因为NAT老化时间的存在,在自适应计算态的时候,暂设计以下步骤在当前心跳区间逼近出最大可用的心跳。 a)变量说明:
b)最大值探测步骤(自适应心跳计算流程): 自适应心跳计算流程如上图所示,经过该流程,会找到必然使心跳失败的curHeart(或者MaxHeart),为了保险起见,我们选择比前一个成功值稍微小一点的值作为后台稳定期的心跳间隔。 影响手机网络测试的因素太多,为了尽量保证测试结果的可靠性,我们使用延迟心跳测试法。在我们重新建立TCP连接后,先使用 短心跳连续成功三次,我们才认为网络相对稳定,可以使用curHeart进行一次心跳测试。图4-2显示了一次有效心跳测试过程。图4-3显示了在没有达到稳定网络环境时,我们会一直使用固定短心跳直到满足三次连续短心跳成功。 使用延迟心跳测试的好处是,可以剔除偶然失败,和网络变化较大的情况(如地铁),使测试结果相对可靠(五次延迟测试确定结论)。同时在网络波动较大的情况,使用短心跳,保证收取消息相对及时。 c)运行时的动态调整策略(已经按测算心跳稳定值后) NAT超时值算出来后,在维持心跳的过程中的策略。 - 无网络、网络时好时坏、偶然失败、NAT超时变小: 在后台稳定期发生心跳发生失败后,我们使用延迟心跳测试法测试五次。如果有一次成功,则保持当前心跳值不变;如果五次测试全失败,重新计算合理心跳值。该过程如图4-4所示,有一点需要注意,每个新建的长连接需要先用短心跳成功维持3次后才用successHeart进行心跳。后台稳定态动态调整心跳策略如下图: - NAT超时变大: 以周为周期,每周三将后台稳定态调至自适应计算态,使用心跳延迟法往后探测心跳间隔。 - successHeart是NAT超时临界值: 因为我们现在选择的是一个比successHeart稍小的值作为稳定值,所以在计算过程中可以避开临界值。当运营商在我们后台稳定期将NAT超时调整为我们当前计算值,那么由于我们每周会去向下探索,所以下一周探测时也可以及时调整正确。 d)冗余Sync和心跳 在用户的一些主动操作以及联网状态改变时,增加冗余Sync和心跳,确保及时收到消息。
八 可能存在的风险及预防措施DHCP租期因素:
9 附录1附录A:NAT超时介绍 因为 IP v4 的 IP 量有限,运营商分配给手机终端的 IP 是运营商内网的 IP,手机要连接 Internet,就需要通过运营商的网关做一个网络地址转换(Network Address Translation,NAT)。简单的说运营商的网关需要维护一个外网 IP、端口到内网 IP、端口的对应关系,以确保内网的手机可以跟 Internet 的服务器通讯。 大部分移动无线网络运营商都在链路一段时间没有数据通讯时,会淘汰 NAT 表中的对应项,造成链路中断。 下表列出一些已测试过的网络的NAT超时时间(更多数据由于测试条件所限没有测到): 长连接心跳间隔必须要小于NAT超时时间(aging-time),如果超过aging-time不做心跳,TCP长连接链路就会中断,Server就无法发送Push给手机,只能等到客户端下次心跳失败后,重建连接才能取到消息。 2附录B:安卓DHCP的租期(lease time)问题目前测试发现安卓系统对DHCP的处理有Bug:
|
地区/网络 | NAT超时时间 |
中国移动3G和2G | 5分钟 |
中国联通2G | 5分钟 |
中国电信3G | 大于28分钟 |
美国3G | 大于28分钟 |
台湾3G | 大于28分钟 |