本文介紹了 Netty 超時機制的原理,以及如何在鏈接閒置時發送一個心跳來維持鏈接。java
Netty 的超時類型 IdleState 主要分爲:git
在 Netty 的 timeout 包下,主要類有:github
其中 IdleStateHandler 包含了讀\寫超時狀態處理,好比服務器
private static final int READ_IDEL_TIME_OUT = 4; // 讀超時 private static final int WRITE_IDEL_TIME_OUT = 5;// 寫超時 private static final int ALL_IDEL_TIME_OUT = 7; // 全部超時 new IdleStateHandler(READ_IDEL_TIME_OUT, WRITE_IDEL_TIME_OUT, ALL_IDEL_TIME_OUT, TimeUnit.SECONDS));
上述例子,在 IdleStateHandler 中定義了讀超時的時間是 4 秒, 寫超時的時間是 5 秒,其餘全部的超時時間是 7 秒。socket
既然 IdleStateHandler 包括了讀\寫超時狀態處理,那麼不少時候 ReadTimeoutHandler 、 WriteTimeoutHandler 均可以不用使用。定義另外一個名爲 HeartbeatHandlerInitializer 的 ChannelInitializer :ide
public class HeartbeatHandlerInitializer extends ChannelInitializer<Channel> { private static final int READ_IDEL_TIME_OUT = 4; // 讀超時 private static final int WRITE_IDEL_TIME_OUT = 5;// 寫超時 private static final int ALL_IDEL_TIME_OUT = 7; // 全部超時 @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new IdleStateHandler(READ_IDEL_TIME_OUT, WRITE_IDEL_TIME_OUT, ALL_IDEL_TIME_OUT, TimeUnit.SECONDS)); // 1 pipeline.addLast(new HeartbeatServerHandler()); // 2 } }
public class HeartbeatServerHandler extends ChannelInboundHandlerAdapter { // Return a unreleasable view on the given ByteBuf // which will just ignore release and retain calls. private static final ByteBuf HEARTBEAT_SEQUENCE = Unpooled .unreleasableBuffer(Unpooled.copiedBuffer("Heartbeat", CharsetUtil.UTF_8)); // 1 @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof IdleStateEvent) { // 2 IdleStateEvent event = (IdleStateEvent) evt; String type = ""; if (event.state() == IdleState.READER_IDLE) { type = "read idle"; } else if (event.state() == IdleState.WRITER_IDLE) { type = "write idle"; } else if (event.state() == IdleState.ALL_IDLE) { type = "all idle"; } ctx.writeAndFlush(HEARTBEAT_SEQUENCE.duplicate()).addListener( ChannelFutureListener.CLOSE_ON_FAILURE); // 3 System.out.println( ctx.channel().remoteAddress()+"超時類型:" + type); } else { super.userEventTriggered(ctx, evt); } } }
服務器代碼比較簡單,啓動後偵聽 8082 端口oop
public final class HeartbeatServer { static final int PORT = 8082; public static void main(String[] args) throws Exception { // Configure the server. EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 100) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new HeartbeatHandlerInitializer()); // Start the server. ChannelFuture f = b.bind(PORT).sync(); // Wait until the server socket is closed. f.channel().closeFuture().sync(); } finally { // Shut down all event loops to terminate all threads. bossGroup.shutdownGracefully(); workerGroup.shutdownGracefully(); } } }
telnet 127.0.0.1 8082
見 https://github.com/waylau/netty-4-user-guide-demos 中 heartbeat包測試