前言
众所周知, 即时通讯, 大部分是采用TCP的方式进行通讯. 因而TCP的链接稳定性对于即时通讯的及时性与消息的到达率有很大的关系.
即时通讯与TCP的关系, 简单理解如下:
- TCP建联 -> 在线前提
- TCP通讯 -> 在线通讯(消息发送+消息在线接收)
- TCP断接 -> 变为离线
在线的前提是
- TCP建联
- client+server在线
离线的前提是:
- client+server有一方离线, 即连接中断
TCP的链接稳定性对于即时通讯有很大的关系.
为什么需要心跳
TCP通讯中的问题
在TCP处于通讯的阶段中.我们是有两个目的, 发送消息和接收消息.
- 发送消息的阶段: 在client发送消息中, 可能server已经断开, client并未感知到, 因而导致识别到server假在线,导致消息发送不出去,另外即便是发现了server断开, 此时再进行重新建联后再进行发送消息, 因而会导致消息因为没有及时的重新建联, 导致消息发送慢
- 接收消息的阶段: 由于client已经断开了链接, 对方不知道, 会导致对方的发送消息处于1的情况.
因而通过心跳, 及时的识别对方在线离线以及保持双方在线, 进而解决消息发送不出去, 以及消息发送慢的问题.
TCP的KeepAlive能否当做作为心跳包
TCPIP 也会发送心跳包, 但是对于应用层来讲, 心跳包的频率和时效性不能够在应用层配置(TCP的心跳包一般固定为7200sTCP/IP详解), 因而会导致死链, 消息接收的实时性存在问题.
TCP/IP的长周期的心跳包在移动端的特殊case
- NAT问题: 移动运营商主动断联 TODO: 信息来源
- 防火墙关闭等
心跳的目的
- 保活, 尽量的保证client与server的连接状态(也是应对移动网络链接不稳定的措施, 移动网络情况下, 可能链接不稳定, 在发现client断联的情况下, 可以快速的重连.)
- 检测在线离线的准确性,保证了消息的消息在线实时可达, 离线快速达
心跳如何设计
1. 怎么样算是心跳
- 通用的网络请求可以当做心跳包: client与server有互动, 便认作是心跳.
- 单独发送心跳包: 在没有网络请求的情况下, 单独发送心跳包
2. 何时发送心跳
- 前后台切换
- 网络连接/重连
- 网络请求, 重置心跳的计时器
3. 心跳的影响因素有那些
- 网络类型: wifi/移动网络
- 设备类型(iOS/Android/PC), 尤其是安卓的设备类型五花八门, 小米,华为, VIVO, OPPO等
- 地域: 国内(GCM不可用), 海外(GCM可用)
- 业务其他诉求(如办公软件的上班时间段, 还是下班时间)
如何设计
心跳是用于client和server双方检测对方是否在线的机制.
因而心跳, 是需要server和client都实现此feature的.
1. server的心跳设计
server侧根据client的上行和下行两个阶段做处理即可.
- client的上行, 即网络请求. 对于收到client的网络请求(包含单独的心跳包), 做在线状态的及时刷新即可
- server的下行, 即同步协议等推送, 如果发现推送不到, 并且在重试后, 依然收不到ack. 便可以更新client的状态为离线.
2. client的心跳设计(动态心跳)
- 定时器(重置, 心跳步长)
- 普通网络请求的横切面(网络成功请求后, 算作心跳)
- 单独的心跳包
- 心跳包与网络请求失败后的步长缩短策略
- 心跳包与网络请求成功后的步长增长策略
竞品的设计
微信的网络保活
之前看到大家在聊各种Java网络框架,而微信实际上都是没用上的。早年的微信,直接通过Java socket 实现。微信v5.0后,考虑各系统平台的统一,开始使用自研c++组件。
长连接实现包括几个要素:
a. 网络切换或者初始化时 server ip 的获取。
b. 连接前的 ip筛选,出错后ip 的抛弃。
c. 维护长连接的心跳。
d. 服务器通过长连notify。
e. 选择使用长连通道的业务。
f. 断开后重连的策略。
今天主题在保活, 我们重点讨论心跳和 notify 机制。
1.1 心跳机制
心跳的目的很简单:通过定期的数据包,对抗NAT超时。以下是部分地区网络NAT 超时统计:
上表说明:
a. GCM无法适应国内2G环境(GCM 28分钟心跳)。
b. 为了兼容国内网络要求,我们至少5分钟心跳一次。
老版本的微信是4.5分钟发送一次心跳,运行良好。
心跳的实现:
a. 连接后主动到服务器Sync拉取一次数据,确保连接过程的新消息。
b. 心跳周期的Alarm 唤醒后,一般有几秒的cpu 时间,无需wakelock。
c. 心跳后的Alarm防止发送超时,如服务器正常回包,该Alarm 取消。
d. 如果服务器回包,系统通过网络唤醒,无需wakelock。
流程基于两个系统特性:
a. Alarm唤醒后,足够cpu时间发包。
b. 网络回包可唤醒机器。
特别是b项,假如Android封堵该特性,那就只能用GCM了。API level >= 23的doze就关闭所有的网络, alarm等。但进入doze条件苛刻,现在6.0普及低,至今微信没收到相关投诉。另Google也最终加入REQUEST_IGNORE_BATTERY_OPTIMIZATIONS权限。
1.1 动态心跳
4.5min心跳周期是稳定可靠的,但无法确定是最大值。通过终端的尝试,可以获取到特定用户网络下,心跳的最大值。
引入该特性的背景:
a. 运营商的信令风暴
b. 运营商网络换代,NAT超时趋于增大
c. Alarm耗电,心跳耗流量。
动态心跳引入下列状态:
a. 前台活跃态:亮屏,微信在前台, 周期minHeart (4.5min) ,保证体验。
b. 后台活跃态:微信在后台10分钟内,周期minHeart ,保证体验。
c. 自适应计算态:步增心跳,尝试获取最大心跳周期(sucHeart)。
d. 后台稳定态:通过最大周期,保持稳定心跳。
自适应计算态流程:
在自适应态:
a. curHeart初始值为minHeart , 步增(heartStep)为1分钟。
b. curHeart 失败5次, 意味着整个自适应态最多只有5分钟无法接收消息。
c. 结束后,如果sucHeart > minHeart,会减去10s(避开临界),为该网络下的稳定周期。
d. 进入稳定态时,要求连接连续三次成功minHeart心跳周期,再使用sucHeart。
稳定态的退出:
sucHeart 会对应网络存储下来, 重启后正常使用。考虑到网络的不稳定,如NAT超时变小,用户地理位置变换。当发现sucHeart 连续5次失败, sucHeart 置为minHeart ,重新进入自适应态。
1.2 notify机制
网络保活的意义在于消息实时。通过长连接,微信有下列机制保证消息的实时。
Sync:
通过Sync CGI直接请求后台数据。Sync 通过后台和终端的seq值对比,判断该下发哪些消息。终端正常处理消息后,seq更新为最新值。
Sync 的主要场景:
a. 长连无法建立时,通过Sync 定期轮询
b. 微信切到前台时,触发Sync(保命机制)
c. 长连建立完成,立即触发Sync,防止连接过程漏消息
d. 接收到Notify 或者 gcm 后,终端触发Sync 接收消息.
Notify:
类似于GCM。通过长连接,后台发出仅带seq的小包,终端根据seq决定是否触发Sync拉取消息。
NotifyData:
在长连稳定, Notify机制正常的情况下(保证seq的同步)。后台直接推送消息内容,节省1个RTT (Sync) 消息接收时间。终端收到内容后,带上seq回应NotifyAck,确认成功。这里会出现Notify和NotifyData状态互相切换的情况:
如NotifyData 后,服务器在没收到NotifyAck,而有新消息的情况下,会切换回到Notify,Sync可能需要冗余之前NotifyData的消息。终端要保证串行处理NotifyData和Sync ,否则seq可能回退。
GCM:
只要机器上有GMS ,启动时就尝试注册GCM,并通知后台。服务器会根据终端是否保持长连,决定是否由GCM通知。GCM主要针对国外比较复杂的网络环境。
GCM心跳策略以及存在的问题
a)用心跳保活长连接,心跳间隔为WIFI下15分钟,数据网络下28分钟。
b)Google可以改变所有Android设备的心跳间隔值(目前还未改变过)。
c)GCM由于心跳间隔固定,并且较长,所以在NAT aging-time设置较小的网络(如联通2G,或有些WIFI环境下)会导致TCP长连接在下一次心跳前被网关释放。造成Push延迟接收。
WhatsApp + Line的心跳策略
Line | GCM | ||
---|---|---|---|
WIFI | 4分45秒 | 3分20秒 | 15分钟 |
手机网络 | 4分45秒 | 7分钟 | 28分钟 |
如何衡量心跳机制做的好坏
通过及时性, 以及 功耗 两个维度 衡量做的好坏.
由于大陆连接不到google, 因而面对的用户为大陆用户时, 海外竞品的实现思路, 仅供借鉴. 用户切到后台后, 在国内除了心跳保活外, 还需要借助厂商的推送通道, 才能保证消息的接收及时性.
参考文章
为什么说基于TCP的移动端IM仍然需要心跳保活?
一文读懂即时通讯应用中的网络心跳包机制:作用、原理、实现思路等
TCP的保活定时器
微信团队原创分享:Android版微信后台保活实战分享(网络保活篇)
移动端IM实践:WhatsApp、Line、微信的心跳策略分析
移动端IM实践:实现Android版微信的智能心跳机制