Netty入门 - Netty心跳机制源码剖析
Netty 心跳机制是保持长连接稳定的一种方法,主要用于检测连接是否仍然活跃,避免因为长时间无数据传输导致连接被关闭。Netty 提供了一个专门的处理器 IdleStateHandler
来实现心跳机制。本文将对 Netty 心跳机制的源码进行剖析。
1. IdleStateHandler
IdleStateHandler
是 Netty 提供的一个处理器,用于检测连接的空闲状态。当一个连接超过指定的时间没有读、写、或读写操作时,会触发相应的事件。
1.1 IdleStateHandler 类
public class IdleStateHandler extends ChannelDuplexHandler {
private final long readerIdleTimeNanos;
private final long writerIdleTimeNanos;
private final long allIdleTimeNanos;
// 省略了构造函数和其他代码
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel().isActive() && ctx.channel().isRegistered()) {
initialize(ctx);
}
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
destroy();
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
initialize(ctx);
super.channelActive(ctx);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
destroy();
super.channelInactive(ctx);
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
readTime = System.nanoTime();
firstReaderIdleEvent = firstAllIdleEvent = true;
ctx.fireChannelRead(msg);
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
promise.addListener(writeListener);
ctx.write(msg, promise);
}
@Override
public void flush(ChannelHandlerContext ctx) throws Exception {
writeTime = System.nanoTime();
firstWriterIdleEvent = firstAllIdleEvent = true;
ctx.flush();
}
private void initialize(ChannelHandlerContext ctx) {
switch (state) {
case 1:
case 2:
return;
}
state = 1;
initOutputChanged(ctx);
lastReadTime = lastWriteTime = System.nanoTime();
schedule(ctx);
}
private void destroy() {
state = 2;
if (readerIdleTimeout != null) {
readerIdleTimeout.cancel(false);
readerIdleTimeout = null;
}
if (writerIdleTimeout != null) {
writerIdleTimeout.cancel(false);
writerIdleTimeout = null;
}
if (allIdleTimeout != null) {
allIdleTimeout.cancel(false);
allIdleTimeout = null;
}
}
private void schedule(final ChannelHandlerContext ctx) {
if (readerIdleTimeNanos > 0) {
readerIdleTimeout = ctx.executor().schedule(new ReaderIdleTimeoutTask(ctx), readerIdleTimeNanos, TimeUnit.NANOSECONDS);
}
if (writerIdleTimeNanos > 0) {
writerIdleTimeout = ctx.executor().schedule(new WriterIdleTimeoutTask(ctx), writerIdleTimeNanos, TimeUnit.NANOSECONDS);
}
if (allIdleTimeNanos > 0) {
allIdleTimeout = ctx.executor().schedule(new AllIdleTimeoutTask(ctx), allIdleTimeNanos, TimeUnit.NANOSECONDS);
}
}
private final class ReaderIdleTimeoutTask implements Runnable {
private final ChannelHandlerContext ctx;
ReaderIdleTimeoutTask(ChannelHandlerContext ctx) {
this.ctx = ctx;
}
@Override
public void run() {
if (!ctx.channel().isOpen()) {
return;
}
long nextDelay = readerIdleTimeNanos;
nextDelay -= System.nanoTime() - lastReadTime;
if (nextDelay <= 0) {
readerIdleTimeout = ctx.executor().schedule(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 {
readerIdleTimeout = ctx.executor().schedule(this, nextDelay, TimeUnit.NANOSECONDS);
}
}
}
// WriterIdleTimeoutTask and AllIdleTimeoutTask are similar
}
1.2 IdleStateEvent 类
IdleStateEvent
是 Netty 提供的一个事件类,用于表示连接的空闲状态。
public class IdleStateEvent {
private final IdleState state;
private final boolean first;
public IdleStateEvent(IdleState state, boolean first) {
this.state = state;
this.first = first;
}
public IdleState state() {
return state;
}
public boolean isFirst() {
return first;
}
}
1.3 IdleState 枚举
IdleState
枚举类表示连接的三种空闲状态。
public enum IdleState {
READER_IDLE,
WRITER_IDLE,
ALL_IDLE
}
2. 实现心跳机制
要实现心跳机制,我们可以自定义一个处理器,继承 ChannelInboundHandlerAdapter
,在连接空闲时发送心跳包。
2.1 HeartbeatHandler 类
public class HeartbeatHandler extends ChannelInboundHandlerAdapter {
private static final Logger logger = LoggerFactory.getLogger(HeartbeatHandler.class);
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
if (evt instanceof IdleStateEvent) {
IdleStateEvent event = (IdleStateEvent) evt;
if (event.state() == IdleState.READER_IDLE) {
logger.info("READER_IDLE 读超时");
} else if (event.state() == IdleState.WRITER_IDLE) {
logger.info("WRITER_IDLE 写超时");
} else if (event.state() == IdleState.ALL_IDLE) {
logger.info("ALL_IDLE 读写超时");
ctx.writeAndFlush(new PingMessage());
}
} else {
super.userEventTriggered(ctx, evt);
}
}
}
2.2 配置心跳检测
在 ChannelPipeline
中添加 IdleStateHandler
和 HeartbeatHandler
。
public class MyChannelInitializer extends ChannelInitializer<SocketChannel> {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new IdleStateHandler(60, 30, 60, TimeUnit.SECONDS));
pipeline.addLast(new HeartbeatHandler());
}
}
总结
Netty 的心跳机制通过 IdleStateHandler
来实现,IdleStateHandler
负责检测连接的空闲状态,并触发相应的事件。我们可以自定义处理器 HeartbeatHandler
,在连接空闲时发送心跳包,确保连接的活跃性。通过源码剖析,我们可以深入理解 Netty 的心跳机制,实现高性能的长连接管理。