netty 中 IdleStateHandler

IdleStateHandler 空閒狀態處理器,是用來檢測當前Handler的ChannelRead()的空閒時間。

構造方法如下

public IdleStateHandler(
            long readerIdleTime, long writerIdleTime, long allIdleTime,
            TimeUnit unit) {
        this(false, readerIdleTime, writerIdleTime, allIdleTime, unit);
    }

 

四個參數如下

1)readerIdleTime:爲讀超時時間(即多長時間沒有接受到客戶端發送數據)

2)writerIdleTime:爲寫超時時間(即多長時間沒有向客戶端發送數據) 

3)allIdleTime:所有類型(讀或寫)的超時時間

4)timeunit:超時時間類型

根據這四個參數IdleStateHandler會啓動不同的定時任務,根據設定的時長去檢測ChannelRead()方法是否被調用,如果沒有被調用。之後則會調用後續handler的userEventTriggered方法去執行一些事情(比如斷開鏈接)

IdleStateHandler 的channelActive方法會在客戶端連接到服務端觸發代碼如下

public void channelActive(ChannelHandlerContext ctx) throws Exception {
        initialize(ctx);
        super.channelActive(ctx);
    }
 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);
        }
    }
 ScheduledFuture<?> schedule(ChannelHandlerContext ctx, Runnable task, long delay, TimeUnit unit) {
        return ctx.executor().schedule(task, delay, unit);
    }

可以看到啓動三個task去執行任務,三個task都實現了Runnable接口,那麼task的run的方法是重點。

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 event = newIdleStateEvent(IdleState.READER_IDLE, first);
                    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);
            }
        }
    }
protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
        //調用Pipeline中的下一個handler的userEventTriggered方法
        ctx.fireUserEventTriggered(evt);
    }

可以看到判斷出超時時會調用下一個Handler的userEventTriggered方法去執行

如果想用這個handler必須要有兩點注意:

1、因爲次handler是監測的是ChannelRead方法,最好放到最開始或者前幾位,千萬要記得一定要傳遞到次handler

2、其後的handler要實現userEventTriggered方法

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章