物联网长连接机制-应用层心跳与超时

csdn ty1972。转载请注明原创出处,谢谢!如果读完觉得有收获的话,欢迎点赞加关注。

长连接概念

这里的连接是指TCP网络传输层经过三次握手建立的连接,长连接是指建立的连接长期保持。无论是物联网平台还是实时信息系统,实时性强需求的报文一般会通过一个单独的双向通道传递,而其他的非实时性质的信息则是通过其他通道传递如大部分在使用的http。因为像类似http的通道只是单向的不能直接供服务器端下方消息,所以,服务端下放消息给客户端时,通常也是走长连接通道,如下图。只有长连接通道存在、不断线,基于该通道上的其他应用消息传递才成为可能。

长连接通道对于系统的作用,一个比较经典的比喻是: 考虑你在和你的家人通过手机说话,拨通电话后两个手机之间建立了一个稳定的通道,你在和家人说话时,都会默认假设通道一直稳定,不会断线,你们说话的全部内容都会从2个手机之间的稳定通道上传给对方。说话时,大部分情况下,你都不会过多考虑通道不稳定。而当2个手机之间真实发生连接不稳定比如信号差时,你才会说“喂喂,能听得到吗”,很多情况下,你的每句话都会只说一遍,只有当对方没有对你的一段话题回复时,或者你不确定对方是否听清你的话时,你才回重复一些话。我们今天谈的长连接即是在网络层面实现这样的一个保证应用稳定对话的技术。因此长连接的价值不言而喻,但是实现稳定的长连接技术却异常困难。

长连接的重要性及实现思路

使用长连接的最大价值在于:在当前连接可用的情况下,每一次应用层的消息传递请求都只是简单的数据发送和接受,免去了 DNS 解析,连接建立等时间,大大加快了请求的速度,同时也有利于接受服务器的实时消息。但前提是连接可用。如果连接无法很好地保持,每次消息传递就会变成撞大运:运气好,通过长连接发送请求并收到反馈。运气差,当前连接已失效,请求迟迟没有收到反馈直到超时,又需要一次连接建立的过程,其效率甚至还不如 HTTP。

基于这个前提,必须要有一种机制用于检测连接的有效性和保持连接恢复机制。同时一些C端应用的需求,也要求C端产品需要在空余时间发送一定的特殊信令,避免连接被回收。而对于服务器而言,能够及时获悉连接可用性也非常重要:一方面服务器需要及时清理无效连接以减轻负载以及警报,另一方面也是业务的需求,如游戏副本中服务器需要及时处理玩家掉线带来的问题或识别掉线率等。

TCP 是一个基于连接的协议,其连接状态是在操作系统级别由一个软件状态机维护,连接完毕后,双方都会处于 established 状态(这里需要注意的是这个established连接状态只是操作系统认为当前还处在连接状态),这之后的状态并不会主动进行变化。这意味着如果上层不进行任何调用,一直使 TCP 连接空闲,那么这个连接虽然没有任何数据,但仍是保持连接状态,一天、一星期、甚至一个月,即使在这期间中间路由崩溃重启无数次。举个现实中经常遇到的例子:当我们 ssh 到自己的 VPS 上,然后不小心踢掉网线,此时的网络变化并不会被 TCP 检测出,当我们重新插回网线,仍旧可以正常使用 ssh,同时此时并没有发生任何 TCP 的重连,也就是说真实情况下已经established的长连接也会存在有效或者无效2种情况,无效的情况下,虽然操作系统中还是established 状态,但是很明显如果此时有消息报文需要传输,这个连接是无法完成的。TCP协议为了保证可靠性,超时的任务会重传,如果问题只是网线接头松了,导致网络不通,此时如果及时将网线接头接好,数据还是能正常到达对端,且TCP的连接依然是established,不会有任何变化。但不是任何时候都这么巧,有时就是某段链路瘫痪了,或者主机挂了,系统异常关闭了等。这时候如果应用系统不能感知到,是件很危险的事情。

TCP KeepAlive心跳保活问题

为了解决刚才的提到这种失效连接,TCP协议实现中,是有保活机制的,也就是TCP的KeepAlive机制(此机制并不是TCP协议规范中的内容,由操作系统去实现),KeepAlive机制开启后,在一定时间内(一般时间为7200s,参数tcpkeepalivetime)在链路上没有数据传送的情况下,TCP层将发送相应的KeepAlive探针以确定连接可用性,探测失败后重试10(参数tcpkeepaliveprobes)次,每次间隔时间75s(参数tcpkeepaliveintvl),所有探测失败后,才认为当前连接已经不可用。这些参数是机器级别,可以调整。不可用的连接会被通知到机器的操作系统,此时操作系统让连接从established改变为time_wait状态,同时重新启动连接。

还存在一个问题,TCP KeepAlive 是用于检测连接的死活,但不能检测通讯双方的存活状态。两者听起来似乎是一个意思,但实际上却大相径庭。考虑一种情况,某台服务器因为某些原因导致负载超高,CPU 100%,无法响应任何业务请求,但是使用 TCP 探针则仍旧能够确定连接状态,这就是典型的连接活着但业务提供方已死的状态,对客户端而言,这时的最好选择就是断线后重新连接其他服务器,而不是一直认为当前服务器是可用状态,一直向当前服务器发送些必然会失败的请求。

应用层心跳+超时

从上面我们可以知道,KeepAlive 并不适用于检测双方存活的场景,这种场景还得依赖于应用层的心跳与超时。应用层心跳有着更大的灵活性,可以控制检测时机,间隔和处理流程,甚至可以在心跳包上附带额外信息。从这个角度而言,应用层的心跳的确是最佳实践。这里应用层的心跳举个例子,比如客户端每隔30s通过长连接通道发送一个心跳请求到服务端,连续失败3次就断开连接。这样算下来最长90s就能发现连接已经不可用,一旦连接不可用,可以重连,也可以做其他的failover处理,比如请求其他服务器。应用层心跳同时能解决“连接活着但业务提供方已死”问题,这时客户端可以切换到连接其他服务器。

性能问题

应用层心跳包机制带来的代价就是性能问题,如果是服务器端服务之间的检测,性能问题还不是矛盾点。但如果是C端用户(载体是手机等移动设备)和服务器交互,应用层心跳则带来了额外的手机耗电和耗流量问题。

微信在早几年推广的过程就因此带来了一下争议,如“微信和运营商的撕B”、“微信对网络影响的技术试验及分析”等问题。所以在满足需求的情况下,尽可能优化方案,则变成了另外一个高深的问题。总之,没有银弹,任何问题的解决都需要一定的代价,作为程序员尽可能优化,降低矛盾点代价。

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180611G09CZG00?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 yunjia_community@tencent.com 删除。

扫码关注云+社区

领取腾讯云代金券