赞
踩
作用:用于空闲连接处理,当出现reader/writer空闲时,触发IdleStateEvent Netty事件,ChannelInboundHandler通过userEventTriggered来捕获这个事件,从而进行处理。
一般我们使用的IdleStateHandler的构造函数有4个参数:
在Netty的Channel中注册两个Handler,注意用于处理IdleStateEvent事件的Handler必须在IdleStateHandler之后注册,否则会出现捕获不到事件的情况(详细原因可以去详细了解Netty的Channel)。
pipeline.addLast("idleStateHandler", new IdleStateHandler(0, 0, 10,TimeUnit.MILLISECONDS));
pipeline.addLast("serverIdleHandler", new ServerIdleHandler());
我们这里的当10秒中没有读写操作时,将会产生一个IdleStateEvent事件,其中IdleState为IdleState.ALL_IDLE,并将其传递给之后的Handler。我们这里在ServerIdleHandler中进行监听这个事件。
其中ServerIdleHandler的源代码如下(需要自己实现)。
下面的示例用于定时关闭空闲的连接,如果发现IdleStateEvent直接将连接关闭。
public class ServerIdleHandler extends ChannelDuplexHandler {
/**
* 如果为IdleStateEvent事件,直接将连接关闭。
*/
@Override
public void userEventTriggered(final ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
ctx.close();
} else {
super.userEventTriggered(ctx, evt);
}
}
}
IdleStateHandler中有三个ScheduledFuture对象,
readerIdleTimeout/writerIdleTimeout/allIdleTimeout,以及三个内部类ReaderIdleTimeoutTask/WriterIdleTimeoutTask/AllIdleTimeoutTask。这里简单介绍一下readerIdleTimeout。
在Channel创建激活之后,将会调用IdleStateHandler中的initialize方法,该方法的源代码如下。
private void initialize(ChannelHandlerContext ctx) { // Avoid the case where destroy() is called before scheduling timeouts. // See: https://github.com/netty/netty/issues/143 switch (state) { case 1: case 2: return; } state = 1; initOutputChanged(ctx); lastReadTime = lastWriteTime = ticksInNanos(); if (readerIdleTimeNanos > 0) { readerIdleTimeout = schedule(ctx, new ReaderIdleTimeoutTask(ctx), readerIdleTimeNanos, TimeUnit.NANOSECONDS); } if (writerIdleTimeNanos > 0) { writerIdleTimeout = schedule(ctx, new WriterIdleTimeoutTask(ctx), writerIdleTimeNanos, TimeUnit.NANOSECONDS); } if (allIdleTimeNanos > 0) { allIdleTimeout = schedule(ctx, new AllIdleTimeoutTask(ctx), allIdleTimeNanos, TimeUnit.NANOSECONDS); } }
initialize方法中通过schedule方法来注册定时任务。下面是schedule的源码。
/**
* ctx.executor() 默认情况下获取的是用于I/O操作的EventLoop,他是一个ScheduledExecutorService,IdleStateHandler使用这个类来执行定时任务,保留定时产生的ScheduledFuture。
* 为了避免Task占用太多的时间影响I/O操作,可以通过配置NioEventLoop的setIoRatio(int ioRatio)方法来调整这个Group的I/O操作和Task操作的时间比例,默认为50%,即I/O操作和Task操作1:1,高连接情况下建议调高这个值。
*
*/
ScheduledFuture<?> schedule(ChannelHandlerContext ctx, Runnable task, long delay, TimeUnit unit) {
return ctx.executor().schedule(task, delay, unit);
}
ReaderIdleTimeoutTask源代码如下:
private final class ReaderIdleTimeoutTask extends AbstractIdleTask { ReaderIdleTimeoutTask(ChannelHandlerContext ctx) { super(ctx); } @Override protected void run(ChannelHandlerContext ctx) { long nextDelay = readerIdleTimeNanos; if (!reading) { nextDelay -= ticksInNanos() - lastReadTime; } if (nextDelay <= 0) { // Reader is idle - set a new timeout and notify the callback. // 已经超时的,注册新的任务用于新一轮的检测 readerIdleTimeout = schedule(ctx, this, readerIdleTimeNanos, TimeUnit.NANOSECONDS); boolean first = firstReaderIdleEvent; firstReaderIdleEvent = false; try { // 核心部分:当出现长时间没有读取数据的时候,产生一个IdleStateEvent事件。 IdleStateEvent event = newIdleStateEvent(IdleState.READER_IDLE, first); /** * 这部分等价于:ctx.fireUserEventTriggered(evt);传递事件给下一个ChannelHandler */ channelIdle(ctx, event); } catch (Throwable t) { ctx.fireExceptionCaught(t); } } else { // Read occurred before the timeout - set a new timeout with shorter delay. // 如果时间还没到,注册剩余时间的任务继续等待 readerIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS); } } }
IdleStateHandler类中还有WriterIdleTimeoutTask和AllIdleTimeoutTask两个事件,同样的用于当长时间没有写或者长时间没有做任何操作触发IdleStateEvent事件,这里不再介绍。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。