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

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

Netty 超時機制的介紹

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

  • ALL_IDLE : 一段時間內沒有數據接收或者發送

  • READER_IDLE : 一段時間內沒有數據接收

  • WRITER_IDLE : 一段時間內沒有數據發送

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

  • IdleStateEvent : 超時的事件

  • IdleStateHandler : 超時狀態處理

  • ReadTimeoutHandler : 讀超時狀態處理

  • 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

效果

20151106-heartbeat

源碼

大家可以加入java交流學習羣:805685193,獲取源碼

裏面有Java高級大牛直播講解知識點 走的就是高端路線 (如果你想跳槽換工作 但是技術又不夠 或者工作上遇到了瓶頸 我這裏有一個JAVA的免費直播課程 講的是高端的知識點基礎不好的誤入喲 只要你有1-5年的開發經驗可以加羣找我要課堂鏈接 注意:是免費的 沒有開發經驗誤入哦)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章