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());

我们这里的当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事件,这里不再介绍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值