Netty 超時機制及心跳程序實現

前言:

本文介紹了 Netty 超時機制的原理,以及如何在連接閒置時發送一個心跳來維持連接。

Netty 超時機制及心跳程序實現

Netty 超時機制的介紹

Netty 的超時類型 IdleState 主要分為:

  1. ALL_IDLE : 一段時間內沒有數據接收或者發送
  2. READER_IDLE : 一段時間內沒有數據接收
  3. WRITER_IDLE : 一段時間內沒有數據發送

在 Netty 的 timeout 包下,主要類有:

  1. IdleStateEvent : 超時的事件
  2. IdleStateHandler : 超時狀態處理
  3. ReadTimeoutHandler : 讀超時狀態處理
  4. WriteTimeoutHandler : 寫超時狀態處理

其中 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 秒。

應用 IdleStateHandler

既然 IdleStateHandler 包括了讀\寫超時狀態處理,那麼很多時候 ReadTimeoutHandler 、 WriteTimeoutHandler 都可以不用使用。定義另一個名為 HeartbeatHandlerInitializer 的 ChannelInitializer :

public class HeartbeatHandlerInitializer extends ChannelInitializer {

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

}

}

使用了 IdleStateHandler ,分別設置了讀、寫超時的時間

定義了一個 HeartbeatServerHandler 處理器,用來處理超時時,發送心跳

定義了一個心跳處理器

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);

}

}

}

定義了心跳時,要發送的內容

判斷是否是 IdleStateEvent 事件,是則處理

將心跳內容發送給客戶端

服務器

服務器代碼比較簡單,啟動後偵聽 8082 端口

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 程序即可:

telnet 127.0.0.1 8082

源碼見https://github.com/waylau/netty-4-user-guide-demos中 heartbeat包

寫在最後:歡迎留言討論,私信“架構資料”有驚喜,加關注,持續更新!


分享到:


相關文章: