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方法