当前位置:   article > 正文

Netty自带的心跳机制——IdleStateHandler

idlestatehandler

一、前言

        Netty提供了对心跳机制的天然支持,心跳可以检测远程端是否存活,或者活跃。

今天我们就一起初识一下Netty4的心跳机制。Netty4.0提供了一个类,名为IdleStateHandler,这个类可以对三种类型的心跳检测。

二、项目中的应用

 上述是项目中应用到的心跳机制关键代码,主要步骤:

  1. 在pipeline中添加IdleStateHandler;
  2. 在IdleStateHandler后面再添加IdleHandler,这个handler是我们自定义的,后面会再分析它

  2.1 自定义的IdleHandler代码

  1. @ChannelHandler.Sharable
  2. public class IdleHandler extends ChannelInboundHandlerAdapter {
  3. private static final Logger LOG = LoggerFactory.getLogger(IdleHandler.class);
  4. @Override
  5. public void userEventTriggered(ChannelHandlerContext ctx, Object paramObject) throws Exception {
  6. LOG.info("userEventTriggered");
  7. if (paramObject instanceof IdleStateEvent) {
  8. IdleState state = ((IdleStateEvent) paramObject).state();
  9. if (state == IdleState.ALL_IDLE) {
  10. //关闭连接
  11. ctx.channel().close();
  12. }
  13. } else {
  14. super.userEventTriggered(ctx, paramObject);
  15. }
  16. }
  17. }

         从上面代码可以看出,IdleHandler继承了ChannelInboundHandlerAdapter,仅重载了userEventTriggered方法。在该方法中,会执行关闭连接的逻辑。

那么问题来了,为什么要加自定义的IdleHandler代码,并重载userEventTriggered方法?
——这个问题答案下面会分析到。

三、IdleStateHandler源码解析

3.1 IdleStateHandler总览

  1. public IdleStateHandler(
  2. long readerIdleTime, long writerIdleTime, long allIdleTime,
  3. TimeUnit unit) {
  4. this(false, readerIdleTime, writerIdleTime, allIdleTime, unit);
  5. }

前三个的参数解释如下:

  1. readerIdleTime:为读超时时间(即测试端一定时间内未接受到被测试端消息
  2. writerIdleTime:为写超时时间(即测试端一定时间内向被测试端发送消息)
  3. allIdleTime:所有类型的超时时间

        这个类主要也是一个ChannelHandler,也需要被载入到ChannelPipeline中,加入我们在服务器端的ChannelInitializer中,在我的项目中(第二节有代码截图):

  1. ...
  2. .addLast(new IdleStateHandler(0, 0, 30, TimeUnit.SECONDS))
  3. .addLast(new IdleHandler())
  4. ...

这段代码的意思是:

        在服务器端会每隔30秒来检查一下channelRead方法被调用的情况,如果在30秒内该链上的channelRead方法都没有被触发,就会调用userEventTriggered方法。

3.2 channelRead、channelActive、channelIdle方法源码

初步地看下IdleStateHandler源码,先看下IdleStateHandler中的channelRead方法:

        在我当前项目的版本中透传和记录调用时间分别放在了channelRead方法和channelReadComplete方法中,在其他版本中,这两处代码都在channelRead中实现,这处小改动是不影响主流程的,为方便比较,我贴上其他版本的代码:

        代码其实表示该方法只是进行了透传,不做任何业务逻辑处理,让channelPipe中的下一个handler处理channelRead方法,但是记录了一下这里的调用时间。

特别说明:

fireChannelRead方法表示传递消息到下一个处理器。

扩展:

fireChannelRead用法 - sunshine_star - 博客园

我们再看看channelActive方法:

这里有个initialize的方法,这是IdleStateHandler的精髓,接着探究:

 这里会触发一个Task,ReaderIdleTimeoutTask,这个task是部分源码是这样的:

        代码逻辑是这样的,用当前时间减去最后一次channelRead方法调用的时间,假如这个结果是6s,说明最后一次调用channelRead已经是6s之前的事情了,你设置的是5s,那么nextDelay则为-1,说明超时了,那么则执行③处代码,会触发channelIdle方法,channelIdle会执行fireUserEventTriggered方法,即下一个处理器的userEventTriggered方法:

 看到这里前面问题的答案就出来了:

为什么要加自定义的IdleHandler代码,并重载userEventTriggered方法?

——因为在自带的IdleStateHandler处理器,在心跳检测超时时,最终会执行下一个handler的userEventTriggered方法来处理。

初略地看下就是这么多了,这就是IdleStateHandler的基本原理了。

四、总结 

简而言之:

        IdleStateHandler这个类会根据你设置的超时参数的类型和值,循环去检测channelRead和write方法多久没有被调用了,如果这个时间超过了你设置的值,那么就会触发对应的事件,read触发read,write触发write,all触发all。

  • 如果超时了,则会调用userEventTriggered方法,且会告诉你超时的类型
  • 如果没有超时,则会循环定时检测,除非你将IdleStateHandler移除Pipeline
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/242218
推荐阅读
相关标签
  

闽ICP备14008679号