当前位置:   article > 正文

IdleStateHandler

idlestatehandler

作用:用于空闲连接处理,当出现reader/writer空闲时,触发IdleStateEvent Netty事件,ChannelInboundHandler通过userEventTriggered来捕获这个事件,从而进行处理。

一、构造函数

一般我们使用的IdleStateHandler的构造函数有4个参数:

  • long readerIdleTimeSeconds:Channel多久没有读取数据会触发IdleStateEvent,其中IdleState为IdleState.READER_IDLE。
  • long writerIdleTimeSeconds:Channel多久没有写数据会触发IdleStateEvent,其中IdleState为IdleState.WRITER_IDLE。
  • long allIdleTimeSeconds:Channel多久没有读和写数据会触发IdleStateEvent,其中IdleState为IdleState.ALL_IDLE。
  • TimeUnit unit:时间单位

二、使用示例

在Netty的Channel中注册两个Handler,注意用于处理IdleStateEvent事件的Handler必须在IdleStateHandler之后注册,否则会出现捕获不到事件的情况(详细原因可以去详细了解Netty的Channel)。

pipeline.addLast("idleStateHandler", new IdleStateHandler(0, 0, 10,TimeUnit.MILLISECONDS));
pipeline.addLast("serverIdleHandler", new ServerIdleHandler());
  • 1
  • 2

我们这里的当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);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

三、简单的源码分析

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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

IdleStateHandler类中还有WriterIdleTimeoutTask和AllIdleTimeoutTask两个事件,同样的用于当长时间没有写或者长时间没有做任何操作触发IdleStateEvent事件,这里不再介绍。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/242200
推荐阅读
相关标签
  

闽ICP备14008679号