Netty 作为一个网络框架,提供了诸多功能,比如编码解码等,Netty 还提供了非常重要的一个服务——-心跳机制 heartbeat。通过心跳检查对方是否有效,这是 RPC 框架中是必不可少的功能。下面我们分析一下 Netty 内部心跳服务源码实现。


Netty 提供了 IdleStateHandler ,ReadTimeoutHandler,WriteTimeoutHandler 三个 Handler 检测连接的有效性,重点分析 IdleStateHandler .
image.png
ReadTimeout 事件和 WriteTimeout 事件都会自动关闭连接,而且,属于异常处理,所以,这里只是介绍以下,我们重点看 IdleStateHandler


IdleStateHandler分析

1.四个属性

  1. private final boolean observeOutput; //是否考虑出站时较慢的情况。默认值是 false
  2. private final long readerIdleTimeNanos;//读事件空闲时间,0 则禁用事件
  3. private final long writerIdleTimeNanos;//写事件空闲时间,0 则禁用事件
  4. private final long allIdleTimeNanos;//读或写空闲时间,0 则禁用事件

2.handlerAdded方法
当该handler被添加到pipeline中时,调用initialize方法
只要给定的参数大于0,就会创建一个定时任务,每个事件都会创建,同时将state状态设置为1,防止重复初始化。调用initOutputChanged方法,初始化“监控出站数据属性”
3.该类内部的三个定时任务类
image.png
这三个定时任务分别对应着读,写,读写事件,共同的父类是AbstractIdleTask


读事件的run方法分析

说明:
1.得到用户设定的超时事件
2.如果读取操作结束了(执行了channelReadComplete方法设置),就用当前时间减去给定时间和最后一次读,如果小于0,说明发生读超时,触发事件,反之则继续放入队列,间隔事件是新的计算事件
3.触发的逻辑是:首先将任务再次放入队列,事件是刚开始设定的时间,返回一个promise对象,用于做取消操作,然后,设置first属性为false,表示下一次读取不再是第一次,这个属性在channelRead方法会被改为true
4.创建一个IdleStateEvent类型的写事件对象,将此对象传递给用户的UserEventTtiggered方法,完成触发事件的操作
5.总的来说,每次读取操作都会记录一个时间,定时任务时间到了,就会计算当前时间和最后一次读的时间的间隔,如果间隔超过了设置的时间,就会触发UserEventTriggered方法


写事件的run方法分析

说明:写任务的run代码逻辑基本与读任务逻辑一样,多了一个较慢数据的判断hasOutputChanged


所有事件的run方法分析

1.表示这个监控着所有的事件,当读写事件发生时,都会记录,代码逻辑和写事件基本一致


Netty心跳机制的小结

1.IdleStateHandler可以实现心跳功能,当服务器和客户端没有任何读写交互时,并超过了给定的时间,就会触发用户handler的userEventTriggered方法,用户可以在这个方法中尝试向对方发送消息,如果发送失败则关闭连接
2.IdleStateHandler 的实现基于EventLoop 的定时任务,每次读写都会记录一个值,在定时任务运行的时候,通过计算当前时间和设置时间和上次时间发生的事件的结果,来判断是否发生空闲
3.内部有三个定时任务,分别对应着读事件,写事件,读写事件
4.同时,IdleStateHandler内部也考虑了一些极端情况:客户端接受缓慢,一次接收数据的速度超过了设置的空闲时间,Netty通过构造方法中的observerOutput属性来决定是否对出站缓冲区的情况进行判断
5.如果出站缓慢,Netty不认为这是空闲,也不触发空闲事件,但是第一次无论如何都是要触发的,因为第一次无法判断是出站缓慢还是空闲,当然,出战缓慢的话可能造成OOM
6.所以,当应用出现了内存溢出,OOM之类,并且写空闲极少发生,那么就需要注意是不是数据出站缓慢了
7.ReadTimeoutHandler 继承自IdleStateHandler,当触发读空闲事件时,就会触发ctx.fireExceptionCaught方法,传入一个RunTimeoutException,然后关闭Socket
8.而WriteTimeoutHandler的实现不是基于IdleStateHandler的,它的原理是,当调用write方法时,会创建一个定时任务,任务内容就是根据传入的promise的情况来判断是否超出了写的时间。当定时任务根据指定时间开始运行,发现promise的isDone方法返回false,表明还没有写完,说明超时了,抛出异常,当write方法完成后,会打断定时任务