Heartbeat and idle detection_java

拍摄于陕西省博物馆,陕博可以了解一下,有军队把守,超多珍贵文物。


Heartbeat and idle detection_java_02

王皓的GitHub:https://github.com/TenaciousDWang


        今天来说一下关于心跳与空闲检测,为什么需要说这两个东西呢?在因为在网络中凡是涉及到端到端的通信都会遇到一个问题就是假死。


        我们知道一个TCP连接建立需要三次握手,断开连接需要四次挥手,原理过程这里就不长篇大论了,通俗一点讲就是:


        三次握手


        男生:做我女朋友吧。

        女生:好的。

        男生:好的。(可以抱女生了)


        四次挥手


        男生:我们分手吧。

        女生:好的,我收拾一下(此时男生不能抱女生了)。

        女生:我收拾好了,我们分手。(此时女生不能抱男生了)。

        男生:好的。


        这里我们的假死主要是在四次挥手阶段,其中任何一个环节出现问题,都可能导致TCP底层断开后,应用层程序并没有捕获到,导致该连接假死,如果存在大量的假死连接没有关闭,就会导致在服务端浪费资源,导致性能下降,最终崩溃。


        服务端假死时,客户端发送数据无响应,一直在等待,影响客户端体验。导致连接假死有可能是程序问题,也有可能是因为网络原因,为了避免此类情况的发生,通常我们会使用心跳机制与空闲检测。


        接下来我们先来说一下服务端,Netty为我们提供了一个IdleStateHandler处理器,使用方法如下:


        

        我们继承父类IdleStateHandler,在构造方法内调用父类的构造函数。


        super(READER_IDLE_TIME, 00, TimeUnit.SECONDS);


        第一个参数是读空闲时间,第二个参数为写空闲时间,第三个参数为读写空闲时间,第四个参数为时间单位,我们设置第一个读空闲时间为15秒,即十五秒内没有读取到该连接的数据,就判断该连接假死。第二和第三个参数我们设置为0,即忽略这两种情况。


        当判断为假死时,Netty会回调channelIdle方法,我们覆写该方法,直接关闭连接,释放资源。


Heartbeat and idle detection_java_03


        我们将空闲检测放在逻辑处理链最头上,优先判断,现在服务端15秒读取不到数据就会判断假死,为了防止误判,服务端需要每15秒内向服务端发送心跳数据。


        我们来为客户端创建一个定时发送心跳数据的处理器HeartBeatTimerHandler与心跳数据包对象。

 

Heartbeat and idle detection_java_04            

Heartbeat and idle detection_java_05


        当连接建立时回调channelActive方法,我们使用executor方法返回当前channel绑定的NIO线程,使用schedule方法执行定时五秒发送一次,取的是服务端15秒空闲检测的三分之一,这个不强制,随意,但是要考虑到两端性能及网络问题。


        现在客户端向服务端定时发送心跳数据,服务端可以知道客户端连接是否假死了,同理客户端也需要知道服务端是否假死,原理基本一致,我们来看一下如何实现,首先来定义一个心跳响应数据包。


Heartbeat and idle detection_java_06


        在服务端创建一个接收心跳数据包,并返回心跳响应数据包的处理器。


Heartbeat and idle detection_java_07


        然后我们为客户端也添加一个IMIdleStateHandler处理器,跟服务端用同一个即可,15秒读取不到服务端心跳相应数据,则判定服务端假死,客户端也关闭连接,释放资源。