心跳包 | 木戈手机站

木戈手机站

当前位置: 首页 » 攻略 » 心跳包

心跳包




什么是心跳包(心跳机制)




先看一下


wiki


上的说法:





心跳包



(英语:Heartbeat)在


计算机科学


中指一种周期性的信号,通过硬件或软件的形式来检测行为的正常与否,或者与


计算机系统


是否一致。[


1]


通常,机器间会每隔几秒钟发送一次心跳包。 如果接收终端没有在指定时间内(通常是几个心跳包发送的时间间隔内)接收到心跳包,发送终端将会被判定发送失败。








简而言之心跳机制是用于检测对端存活的一种常用方式。


有点类似icu里面的心跳检测机(服务端),你的心脏(客户端)跳一下,他就更新一下状态,认为你还活着,你要太长时间没跳,他就认为你已经不行了,然后发出



bi



的警告。



在网络中,心跳的作用是,在一种需要对端保持连接的状态中,并且存在无法通过上一次的请求判断当前的状态(虽然心跳也不能保证下一次发送成功,但是现实是,我上一次请求是12h前发的,所以你现在还在不在),心跳包可以单独检测对端的存活状态,从而防止发送无用的数据包,另外在分布式系统中,可以避免将数据发送到不可用的节点上(这是比较麻烦的,我成功地把包发到一个不可用的节点上,它给我反错误了,我怎么办,发给其它节点吗,会造成双花吗?)


当然心跳机制不会完美地解决上面的这些问题(毕竟我也不能保证这一秒你的心还在跳,下一秒你就一定还活着),在高可用的系统中还是需要设计另外的机制来防止双花。


另外值得说的一点是:我们不能在网络中发送过多的心跳包,因为在很多时候,网络也是一直有限的资源(心跳虽好,可不要贪杯o),当然也有设计感觉网络情况动态调整的心跳机制,不过那就涉及一些网络底层的东西了。




常见的心跳包






keepalive




说到常见的心跳包,就不得不说tcp keepalive机制了


依然是wiki:



传输控制协议


(TCP)存活包为可选特性,且默认关闭。[


1]


存活包内没有数据。在


以太网


网络中,存活包的大小为最小长度的几帧(64字节[


2]


)。协议中[


3]


,还有三个与存活包相关的参数:












存活时长



(英语:Keepalive time)即空闲时,两次传输存活包的持续时间。TCP存活包时长可手动配置,默认不少于2个小时。




存活间隔



(英语:Keepalive interval)即未收到上个存活包时,两次连续传输存活包的时间间隔。




存活重试次数



(英语:Keepalive retry)即在判断远程主机不可用前的发送存活包次数。当两个主机透过TCP/IP协议相连时,TCP存活包可用于判断连接是否可用,并按需中断。


多数支持TCP协议的主机也同时支持TCP存活包。每个主机按一定周期向其他主机发送TCP包来请求回应。若发送主机未收到特定主机的回应(ACK),则将从发送主机一侧中断连接。 若其他主机在连接关闭后发送TCP存活包,关闭连接的一方将发送RST包来表明旧连接已不可用。其他主机将关闭它一侧的连接以新建连接。


空闲的TCP连接通常会每隔45秒或60秒发送一次存活包。在未连续收到三次ACK包时,连接将中断。此行为因主机而异,如默认情况下的Windows主机将在7200000ms(2小时)后发送首个存活包,随后再以1000ms的间隔发送5个存活包。若任意存活包未收到回应,连接将被中断。


keepalive作为最基础的心跳机制,其设计已经融入tcp协议中了。


  • wireshark keepalive捉包分析




TLS的心跳机制与心脏出血漏洞




TLS心跳原理


rfc 6520


这里就之间放rfc的原文了,感兴趣的可以去读读看。



简单来讲TLS心跳拓展主要解决的是在tls链路中,判断对方存活需要进行一次tls协商(这是比较费时),这个心跳拓展的主要目的是通过一个简单的心跳过程来保留tls链路的存活,在之前是使用tcp的keepalive来做的,但tcp的keepalive只能保证tcp链路的可用性。


看完这篇rfc,有两个比较有意思的点


对于每个心跳包,我们需要给他一个payload,而服务端返回的时候需要原封不动的返回这个payload。这么做我猜测是外来解决网络超时的问题,防止我受到之前的包


不需要时时刻刻的发送心跳包,感觉rfc的定义,我们只需要在网络空闲的时候发送心跳包,而在链路中有请求的情况下则不需要发送请求包。




  • 心脏出血漏洞


    (ok,我还是懒得写,连接安排上)





IM系统中的心跳机制




IM系统(通讯系统)中的心跳机制主要是获取用户在线状态,以及向用户推送数据用


与前面两种心跳类似,不过IM系统需要面对一个麻烦的东西 — NAT(当然TLS的心跳也有考虑NAT的因素)。对于IM系统,本质是是一个C/S架构的系统,而大部分的C都是没有独立IP的,与server通讯,全靠NAT分配的临时IP与端口,而NAT的反配权又不是C端掌控的,实际上是运营商在控制NAT的分配与释放。同时IM系统中一般C的数量会是S数量的几千-几万倍,维护心跳状态将会耗费大量的资源,不过值得庆幸的是,IM实际上是一种弱可用的系统,服务端不需要对客户端的心跳做出反应,也不需要向客户端发送心跳包,有点类似于UDP,客服端发出去就不管了。当然IM的心跳机制还是颇为复杂的,而他的复杂也不是我想要了解的信息,所以这里只给出一片作者认为还可以的


博客








应用层上的心跳包




上面谈到的心跳机制基本上都是网络层面的心跳机制,更多的是确认一个链路是否还可用。当然我们可以把这个链路再抽象一下,比如在一个对等网络中,你连接了某个数据库的资源,我连接了另一个数据库的资源,而我们需要保证相互之间 到数据库的链路是连通的。那么这就不是简单的tcp keepalive这种模式能解决的问题了。我们没法在网络传输模块完成整个心跳过程,网络层甚至不知道数据库是什么,所以这个请求必须上抛到应用层,而应用层在根据自身情况,去找数据库拿状态(这里有个情况,为什么不让数据库也跳起来,主要是浪费资源,心跳是检测活性,如果没有client,实际上也用不到数据库的活性,对于心跳包中复杂的请求,应该被动的等待需要的时候再去操作,而不是在主动地推送自己的状态)。


当然这还会有一些问题我们包心跳的逻辑全部放在应用层,是不是对心跳是不很友好,这会导致应用层的逻辑与网络层的逻辑耦合在一起,本来应该网络层做的心跳,可所有的逻辑都在应用层,网络层就像完全没有心跳这回事。




心跳方案的设计




说这么多,最后我们还是回到现实,最近我的一开发任务,为我们的分布式系统添加一个心跳检测机制。


简单描述一下我们的系统:


一个分布式的系统,每个peer会连接一个或多个资源,一个资源会被多个peer连接,当peer受到请求后会随即的把请求发送到他知道能处理这个请求的peer中。peer之间使用json_rpc通行(用rpc协议是因为我们这个过程实际上就是一个远程调用)。


比如我是a,我知道b,c,d能处理


x


的请求,我会随即的把请求发到b,c,d某个peer中(或者发送到第一个peer)。



现在遇到的一个问题是,如果b,c,d中间有人挂机了,a是不知道的,而a还会随即的把请求发到一个节点里面。


我现在的设计方案是,在rpc模块实现心跳的逻辑,包括自动发送心跳包,判断返回结果是否正常,对于多次心跳异常的节点进行处理,(对方节点死了也要周期性的那,用于复活),也就是心跳的主要逻辑在rpc中实现(网络模块),它用于控制心跳的评论等等,然后在rpc中抽象出两个接口,HeartbeatHandler,HeartbeatClient


  • HeartbeatHandler 接口表示心跳服务端处理的接口


  • HeartbeatClient 接口表示心跳客户端的接口: 客户端接口只需要提供两个方法,一个属如果构造心跳请求的接口,一个属如何处理心跳结果的接口


在server 启动时,将一个HeartbeatHandler的接口注册到server中,在client实例化的时候设置HeartbeatClient 并开启心跳,而心跳的流程控制还在网络模块中。


在我的这个业务中,HeartbeatHandler接口在收到心跳请求会,回去找当前节点连接的资源获取状态,让后返回资源是否可用的结果给clinet


client根据结果来刷新自己的路由表,确保下次请求发送到一个状态最健康的节点上。


**WRAN 以上文章只是出自我的初步调研,若有疏漏,还请同好们多多指正**

猜你喜欢
本类排行