Netty自带的心跳机制——IdleStateHandler
作者:互联网
一、前言
Netty提供了对心跳机制的天然支持,心跳可以检测远程端是否存活,或者活跃。
今天我们就一起初识一下Netty4的心跳机制。Netty4.0提供了一个类,名为IdleStateHandler,这个类可以对三种类型的心跳检测。
二、项目中的应用
上述是项目中应用到的心跳机制关键代码,主要步骤:
- 在pipeline中添加IdleStateHandler;
- 在IdleStateHandler后面再添加IdleHandler,这个handler是我们自定义的,后面会再分析它
2.1 自定义的IdleHandler代码
@ChannelHandler.Sharable
public class IdleHandler extends ChannelInboundHandlerAdapter {
private static final Logger LOG = LoggerFactory.getLogger(IdleHandler.class);
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object paramObject) throws Exception {
LOG.info("userEventTriggered");
if (paramObject instanceof IdleStateEvent) {
IdleState state = ((IdleStateEvent) paramObject).state();
if (state == IdleState.ALL_IDLE) {
//关闭连接
ctx.channel().close();
}
} else {
super.userEventTriggered(ctx, paramObject);
}
}
}
从上面代码可以看出,IdleHandler继承了ChannelInboundHandlerAdapter,仅重载了userEventTriggered方法。在该方法中,会执行关闭连接的逻辑。
那么问题来了,为什么要加自定义的IdleHandler代码,并重载userEventTriggered方法?
——这个问题答案下面会分析到。
三、IdleStateHandler源码解析
3.1 IdleStateHandler总览
public IdleStateHandler(
long readerIdleTime, long writerIdleTime, long allIdleTime,
TimeUnit unit) {
this(false, readerIdleTime, writerIdleTime, allIdleTime, unit);
}
前三个的参数解释如下:
- readerIdleTime:为读超时时间(即测试端一定时间内未接受到被测试端消息
- writerIdleTime:为写超时时间(即测试端一定时间内向被测试端发送消息)
- allIdleTime:所有类型的超时时间
这个类主要也是一个ChannelHandler,也需要被载入到ChannelPipeline中,加入我们在服务器端的ChannelInitializer中,在我的项目中(第二节有代码截图):
...
.addLast(new IdleStateHandler(0, 0, 30, TimeUnit.SECONDS))
.addLast(new IdleHandler())
...
这段代码的意思是:
在服务器端会每隔30秒来检查一下channelRead方法被调用的情况,如果在30秒内该链上的channelRead方法都没有被触发,就会调用userEventTriggered方法。
3.2 channelRead、channelActive、channelIdle方法源码
初步地看下IdleStateHandler源码,先看下IdleStateHandler中的channelRead方法:
在我当前项目的版本中透传和记录调用时间分别放在了channelRead方法和channelReadComplete方法中,在其他版本中,这两处代码都在channelRead中实现,这处小改动是不影响主流程的,为方便比较,我贴上其他版本的代码:
代码其实表示该方法只是进行了透传,不做任何业务逻辑处理,让channelPipe中的下一个handler处理channelRead方法,但是记录了一下这里的调用时间。
特别说明:
fireChannelRead方法表示传递消息到下一个处理器。
扩展:
我们再看看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
标签:Netty,超时,IdleHandler,channelRead,userEventTriggered,自带,IdleStateHandler,方法 来源: https://blog.csdn.net/moneywenxue/article/details/120868468