这是近期参与的一个云推送项目调研的一点东西,贴上来做个备份:)
如需转载,请注明出处:)
综述
所谓的心跳机制,就是周期性的给服务器发送一个数据包,避免该连接上长期没有数据传送,被传输网络中的防火墙或者NAT等设备中断,就是当一个TCP会话的两个连续报文到达防火墙的时间间隔大于该会话的保持时间时,为了保证网络的安全性,防火墙会从会话表中删除相应的会话信息,后续报文到达防火墙后,防火墙根据自身的转发机制,丢弃该报文,导致通讯中断,必须重新建立连接。
为了保持这个长连接不被防火墙删除,需要在没有数据传送时,通过周期的发送心跳信息来保持会话连续。即心跳的目的:
保活链接。
检测设备网络状态,保持通讯顺畅。
心跳机制对于客户端来说,是需要付出一些代价的,比如:
网络流量。
电量。
要减少损耗,一方面要减少每次心跳发送的数据量,更重要的是要减少发送心跳包的次数,因此心跳周期的设置成为关键要素:
如果心跳周期过短,那么会频繁的发送心跳,会造成待机时间减少,增加网络流量。
如果心跳周期设置过长,那么如果网络出现问题的时候就无法及时的检测的异常并清理资源,发起重连,导致推送消息的延迟,更关键的是过长的心跳会导致防火墙的强制关闭。
所以需要进行权衡,找到一个最合适的心跳时间,但由于网络情况的复杂,我们无法预先知道这个最合适的时间,所以需要找到一种自适应(adaptive)的智能算法来进行猜想。
算法需要考虑到以下场景:
当前的通讯网络拥塞造成的心跳超时或者服务器挂掉。
用户切换网络或者防火墙会话保持时间改变。
初步设计
首先需要确定心跳间隔的最小值min和最大值max,单位为秒,这是我们经过算法得到的心跳间隔不可以超过这个范围。
max和min的确定需要经过实际的统计和测试才能得出,可先初步指定,后续酌情设置,最终的心跳时间会落在这个范围之内。
算法在客户端上实现,因为心跳消息是由客户端发起的,只有客户端能检测到心跳消息的发送失败。
算法流程简述如下:
1)客户端请求goload接口,得到上述提到的min和max,还有T和N和M,后续会介绍。
客户端还需维护3个变量,分别为:当前的心跳间隔cur,min'和max',后续会介绍到。
初始设置如下:
cur = (min + max) / 2
min' = min
max' = max
2)连接tcp server,第一次发送心跳后间隔cur时间后发送第二次心跳。
3)如果第二次发送后服务端响应成功,那么代表网络顺畅且当前的时间间隔没有被NAT杀掉,那么更新数据:
min' = cur
cur = (max' + cur) / 2
4)以cur作为心跳间隔继续发送心跳。
5)如果发送心跳没有得到服务端响应(客户端应会设置一个read超时时间,需要酌情设置),则代表出现网络问题,可能是网络拥塞出现了读超时,那么客户端可以重新试图发送心跳N次,间隔为M秒,N和M酌情设置,如果发现还是超时,则尝试重连;也可能是当前的心跳间隔比NAT会话时间大,或者是设备的网络不可用,那么需要进行重连,重连前需要更新下列数据并回到第2个步骤:
max' = cur
cur = (cur + min') / 2
6)在经过了若干次计算之后,会得到一个最优的心跳间隔,之后就不需要进行计算了,条件如下:
cur - min < T or max - cur < T
T为一个阈值,第1点有提到,可酌情设置。
结论
使用智能心跳策略有利于节省电量和流量,百度也支持了这种策略,但有以下一些小问题:
在探测的阶段的重连次数会多一些。
增大客户端的逻辑处理。
在用户网络情况多变的场景也许不稳定。