先看一個例子.promise
有一個簡單 Serversocket
public class SimpleServer { public static void main(String[] args) throws Exception { EventLoopGroup bossGroup = new NioEventLoopGroup(1); EventLoopGroup workerGroup = new NioEventLoopGroup(); ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_REUSEADDR, true) .childOption(ChannelOption.SO_SNDBUF, 1024 * 1024) .childHandler(new ChannelInitializer<NioSocketChannel>() { @Override protected void initChannel(NioSocketChannel ch) throws Exception { ch.pipeline().addLast(new SimpleDuplex1()); ch.pipeline().addLast(new SimpleDuplex2()); ch.pipeline().addLast(new SimpleServerHandler()); } }); b.bind(8090).sync().channel().closeFuture().sync(); } }
Handler 詳情以下ide
public class SimpleDuplex1 extends ChannelDuplexHandler { @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { System.out.println("---- write 1 ----"); super.write(ctx, msg, promise); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("---- read 1 ----"); super.channelRead(ctx, msg); } } public class SimpleDuplex2 extends ChannelDuplexHandler { @Override public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { System.out.println("---- write 2 ----"); super.write(ctx, msg, promise); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("---- read 2 ----"); super.channelRead(ctx, msg); } }
public class SimpleServerHandler extends ChannelDuplexHandler { @Override public void channelRead(ChannelHandlerContext ctx, final Object msg) throws Exception { ctx.channel().writeAndFlush(ByteBufAllocator.DEFAULT.buffer().writeBytes("OK".getBytes())).addListener(ChannelFutureListener.CLOSE); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println("----- INACTIVE -----"); super.channelInactive(ctx); } @Override public void close(ChannelHandlerContext ctx, ChannelPromise future) throws Exception { System.out.println("----- CLOSE -----"); super.close(ctx, future); } }
啓動 Server 之後, 使用 telnet 發送數據查看執行結果oop
---- read 1 ----
---- read 2 ----
成功
---- write 2 ----
---- write 1 ----
----- CLOSE -----
----- INACTIVE -----
1. 先來看看執行順序, 可見, inbound 的順序是跟 add 順序一致的, 而 outbound 的順序是跟 add 順序相反的spa
以及, read 的 IO 觸發順序是 "socketChannel.read() -> 順序 handler -> TailContext.channelRead().releaseMsg"debug
而 write 的 IO 觸發順序是 "逆序 handler -> HeadContext.socketChannel.write()"code
也就是說 read 是先觸發 socket 的 read IO 時間, 再進入 handler, 而若是咱們最後一個 handler 未能徹底處理消息, 調用了 super.channelRead, 則會進入 TailContext. 此時TailContext 會打出 debug 消息告訴你消息進入了最後一個 Handler 而未被處理. 由於通常來說都應該在本身的 handler 裏把消息處理掉. 而不是讓他進入到默認 handler 裏.blog
而對於 write 來講, 則是先進入自定義 handler, 最後在進入 HeadContext 觸發 IO 時間事件
2. 再來講說 close 與 channelInactiveip
前面說到了. Outbound 的順序是最後才執行到 HeadContext 來執行實際的 IO 操做, close 也是同樣, 當你調用 channle.close 的時候, 先會通過你的 handler . 最後調用 HeadContext.socketChannel.close(). 因此, 在咱們的 Handler 中, 先會打印 "---- CLOSE ----" 而後再調用實際的 socketChannel.close. 最後, 當 close 成功時, 觸發 ChannelInactive 時間.
因此說 close 與 channelInactive 的關係是 close 是主動關閉 channel 的動做, 而 channelInactive 是關閉成功後收到通知的事件.