定义
心跳包就是在客户端和服务器之间定时通知对方自己状态的一个自己定义的命令字,按照一定的时间间隔发送,类似心跳,所以叫做心跳包。
功能描述
用来判断对方是否正常运行,采用定时发送简单的通讯包,如果在指定时间内未收到对方响应,则判断对方已经离线。用于检测TCP的异常断开。基本原因是服务器端不能有效的判断客户端是否在线,也就是说,服务器无法区分客户端是长时间空闲,还是已经掉线的情况。所谓心跳包就是客户端定时发送简单的信息给服务器端告诉它我还在而已。
代码就是每个几分钟发送一个固定信息给服务端,服务端收到后回复一个固定信息,如果服务端几分钟内没有收到客户端信息,则认为客户端断开。
比如有些通信软件长时间不使用,要想知道它的状态是在线还是离线就需要心跳包,定时发包收包。
发包方:可以是客户端也可以是服务端,看哪边实现方便即可。一般是客户端,服务器端也可以发送心跳包。一般来说,出于效率的考虑,是由客户端主动向服务端发包,而不是服务器向客户端发。客户端每隔一段时间发一个包,使用TCP的,用send发,使用UDP的,使用sendto发,服务器收到后,就知道当前客户端还处于“活着”的状态,否责,如果隔一定时间未收到这样的包,则服务器认为客户端已经断开,进行响应的客户端断开逻辑处理。
实现
大部分CS的应用需要心跳机制。心跳机制一般在Server和Client都要实现,两者实现原理基本一样。Client不关心性能,怎么做都行。
如果应用是基于TCP的,可以简单地通过SO_KEEPALIVE实现心跳。TCP在设置的KeepAlive定时器到达时向对端发一个检测TCP segment,如果没收到ACK或RST,尝试几次后,就认为对端已经不存在,最后通知应用程序。这里有个缺点是,Server主动发出检测包,对性能有点影响。
应用自己实现
Client启动一个定时器,不断发心跳;
Server收到心跳后,给个回应;
Server启动一个定时器,判断Client是否存在,判断方法这里列两种:时间差和简单标志。
-
时间差策略
收到一个心跳后,记录当前时间(记为recvedTime)。
判断定时器时间到达,计算多久没收到心跳的时间(T)=当前时间 - recvedTime(上面记录的时间)。如果T大于某个设定值,就可以认为Client超时了。 -
简单标志
收到一个心跳后,设置连接标志为true;
判断定时器时间到达,查看所有的标志,false的,认为对端超时了;true的将其设成false。
这种方法比上面简单一些,但检测某个Client是否离线的误差有点大。
总的来说,心跳包主要也就是用于长连接的保活和断线处理。一般的应用下,判定时间在30-40秒比较不错。如果实在要求高,那就在6-9秒
------ Nacos心跳机制 ------
一、源码流程图
二、客户端
当nacos进行服务注册的时候,NacosServiceRegistry.class会调用register()方法进行服务注册,该方法中调用了namingService.registerInstance()方法进行服务注册的逻辑。
NacosNamingService实现了NamingService的接口;然后在namingService.registerInstance()方法中,会做两件事情,第一件事就是组装心跳包BeatInfo,并且发送心跳:
NacosNamingService中的构造函数,会调用init()方法,然后在init方法中会执行一个BeatReactor线程
NacosNamingService中的构造函数和init()方法:
BeatReactor的构造函数中创建了一个ScheduledExecutorService线程操作对象,在里面执行了一个线程操作,BeatTask线程,然后在BeatTask线程中调用了sendBeat()方法,将心跳包作为参数;
BeatTask线程操作:调用sendBeat()方法
在sendBeat()方法中,通过http服务,调用了InstanceController.beat()方法,进行心跳的确认:
三、服务端
在服务端的心跳接口,InstanceController.beat内,会判断实例是否存在,如果不存在,会重新注册。(如网络不通导致实例在服务端被下线,或服务端重启临时实例丢失)
然后,执行service.processClientBeat(clientBeat)方法,调用一个线程任务
在该任务中,将上次的心跳时间,设置为当前时间
至此,nacos发送心跳的过程就到此结束。