Netty心跳服务源码剖析
Netty提供了IdleStateHandler,ReadTimeoutHandler,WriteTimeoutHandler三个Handler检测连接有效性,IdleStateHandler,当连接的空闲时间太长时,将会触发一个IdleStateEvent事件。然后,你可以通过你的ChannelInboundHandler中重写userEventTrigged方法来处理该事件,我们分析一下IdleStateHandler。
四个关键属性
private final boolean observeOutput;//是否考虑出站时较慢的情况,默认是false
private final long readerIdleTimeNanos;//读事件空闲时间,0则禁用事件
private final long writerIdleTimeNanos;//写事件空闲时间,0则禁用事件
private final long allIdleTimeNanos;//读或写空闲时间,0则禁用事件
handlerAdded方法
当该handler被添加到pipeline中时,则调用initialize方法
private void initialize(ChannelHandlerContext ctx) {
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) {
// 新建一个AllIdleTimeoutTask用于检测
allIdleTimeout = schedule(ctx, new AllIdleTimeoutTask(ctx),
allIdleTimeNanos, TimeUnit.NANOSECONDS);
}
}
AllIdleTimeoutTask的代码
protected void run(ChannelHandlerContext ctx) {
// 将nextDelay设置为认为是空闲 的时长,
// 即allIdleTimeNanos内无读写即是空闲
long nextDelay = allIdleTimeNanos;
if (!reading) {
// 用 allIdleTimeNanos减去已空闲时长得到nextDelay
nextDelay -= ticksInNanos() - Math.max(lastReadTime, lastWriteTime);
}
if (nextDelay <= 0) {
// 说明已空闲时长大于allIdleTimeNanos,触发事件
// Both reader and writer are idle - set a new timeout and
// notify the callback.
// 开启下一个定时检测是否空闲
allIdleTimeout = schedule(ctx, this, allIdleTimeNanos, TimeUnit.NANOSECONDS);
boolean first = firstAllIdleEvent;
firstAllIdleEvent = false;
try {
if (hasOutputChanged(ctx, first)) {
// 如果是出站数据缓慢,则不触发事件
return;
}
IdleStateEvent event = newIdleStateEvent(IdleState.ALL_IDLE, first);
// 里面会调用ctx.fireUserEventTriggered(evt)
channelIdle(ctx, event);
} catch (Throwable t) {
ctx.fireExceptionCaught(t);
}
} else {
// Either read or write occurred before the timeout - set a new
// timeout with shorter delay.
// 说明不需触发事件,开启下一个定时检测是否空闲
allIdleTimeout = schedule(ctx, this, nextDelay, TimeUnit.NANOSECONDS);
}
}
}
记录上一次活动时间
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
if ((readerIdleTimeNanos > 0 || allIdleTimeNanos > 0) && reading) {
lastReadTime = ticksInNanos();
reading = false;
}
ctx.fireChannelReadComplete();
}