IM即时通讯-8-如何设计心跳

前言

众所周知, 即时通讯, 大部分是采用TCP的方式进行通讯. 因而TCP的链接稳定性对于即时通讯的及时性与消息的到达率有很大的关系.
即时通讯与TCP的关系, 简单理解如下:

  1. TCP建联 -> 在线前提
  2. TCP通讯 -> 在线通讯(消息发送+消息在线接收)
  3. TCP断接 -> 变为离线

在线的前提是

  1. TCP建联
  2. client+server在线

离线的前提是:

  1. client+server有一方离线, 即连接中断

TCP的链接稳定性对于即时通讯有很大的关系.

为什么需要心跳

TCP通讯中的问题

在TCP处于通讯的阶段中.我们是有两个目的, 发送消息和接收消息.

  1. 发送消息的阶段: 在client发送消息中, 可能server已经断开, client并未感知到, 因而导致识别到server假在线,导致消息发送不出去,另外即便是发现了server断开, 此时再进行重新建联后再进行发送消息, 因而会导致消息因为没有及时的重新建联, 导致消息发送慢
  2. 接收消息的阶段: 由于client已经断开了链接, 对方不知道, 会导致对方的发送消息处于1的情况.

因而通过心跳, 及时的识别对方在线离线以及保持双方在线, 进而解决消息发送不出去, 以及消息发送慢的问题.

TCP的KeepAlive能否当做作为心跳包

TCPIP 也会发送心跳包, 但是对于应用层来讲, 心跳包的频率和时效性不能够在应用层配置(TCP的心跳包一般固定为7200sTCP/IP详解), 因而会导致死链, 消息接收的实时性存在问题.

TCP/IP的长周期的心跳包在移动端的特殊case

  1. NAT问题: 移动运营商主动断联 TODO: 信息来源
  2. 防火墙关闭等

心跳的目的

  1. 保活, 尽量的保证client与server的连接状态(也是应对移动网络链接不稳定的措施, 移动网络情况下, 可能链接不稳定, 在发现client断联的情况下, 可以快速的重连.)
  2. 检测在线离线的准确性,保证了消息的消息在线实时可达, 离线快速达

心跳如何设计

1. 怎么样算是心跳

  1. 通用的网络请求可以当做心跳包: client与server有互动, 便认作是心跳.
  2. 单独发送心跳包: 在没有网络请求的情况下, 单独发送心跳包

2. 何时发送心跳

  1. 前后台切换
  2. 网络连接/重连
  3. 网络请求, 重置心跳的计时器

3. 心跳的影响因素有那些

  1. 网络类型: wifi/移动网络
  2. 设备类型(iOS/Android/PC), 尤其是安卓的设备类型五花八门, 小米,华为, VIVO, OPPO等
  3. 地域: 国内(GCM不可用), 海外(GCM可用)
  4. 业务其他诉求(如办公软件的上班时间段, 还是下班时间)

如何设计

心跳是用于client和server双方检测对方是否在线的机制.
因而心跳, 是需要server和client都实现此feature的.

1. server的心跳设计

server侧根据client的上行和下行两个阶段做处理即可.

  1. client的上行, 即网络请求. 对于收到client的网络请求(包含单独的心跳包), 做在线状态的及时刷新即可
  2. server的下行, 即同步协议等推送, 如果发现推送不到, 并且在重试后, 依然收不到ack. 便可以更新client的状态为离线.

2. client的心跳设计(动态心跳)

  1. 定时器(重置, 心跳步长)
  2. 普通网络请求的横切面(网络成功请求后, 算作心跳)
  3. 单独的心跳包
  4. 心跳包与网络请求失败后的步长缩短策略
  5. 心跳包与网络请求成功后的步长增长策略

竞品的设计

微信的网络保活

之前看到大家在聊各种Java网络框架,而微信实际上都是没用上的。早年的微信,直接通过Java socket 实现。微信v5.0后,考虑各系统平台的统一,开始使用自研c++组件。

长连接实现包括几个要素:

a. 网络切换或者初始化时 server ip 的获取。

b. 连接前的 ip筛选,出错后ip 的抛弃。

c. 维护长连接的心跳。

d. 服务器通过长连notify。

e. 选择使用长连通道的业务。

f. 断开后重连的策略。

今天主题在保活, 我们重点讨论心跳和 notify 机制。

1.1 心跳机制

心跳的目的很简单:通过定期的数据包,对抗NAT超时。以下是部分地区网络NAT 超时统计:

IM即时通讯-8-如何设计心跳_第1张图片

上表说明:

a. GCM无法适应国内2G环境(GCM 28分钟心跳)。

b. 为了兼容国内网络要求,我们至少5分钟心跳一次。

老版本的微信是4.5分钟发送一次心跳,运行良好。

心跳的实现:

IM即时通讯-8-如何设计心跳_第2张图片

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. 后台稳定态:通过最大周期,保持稳定心跳。

自适应计算态流程:

IM即时通讯-8-如何设计心跳_第3张图片

在自适应态:

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的心跳策略

WhatsApp Line GCM
WIFI 4分45秒 3分20秒 15分钟
手机网络 4分45秒 7分钟 28分钟

如何衡量心跳机制做的好坏

通过及时性, 以及 功耗 两个维度 衡量做的好坏.

IM即时通讯-8-如何设计心跳_第4张图片
由于大陆连接不到google, 因而面对的用户为大陆用户时, 海外竞品的实现思路, 仅供借鉴. 用户切到后台后, 在国内除了心跳保活外, 还需要借助厂商的推送通道, 才能保证消息的接收及时性.

参考文章

为什么说基于TCP的移动端IM仍然需要心跳保活?
一文读懂即时通讯应用中的网络心跳包机制:作用、原理、实现思路等
TCP的保活定时器
微信团队原创分享:Android版微信后台保活实战分享(网络保活篇)
移动端IM实践:WhatsApp、Line、微信的心跳策略分析
移动端IM实践:实现Android版微信的智能心跳机制

你可能感兴趣的:(即时通讯,端到端解决方案,tcp/ip,企业微信,钉钉,IM,即时通讯)