Channel,ChannelHandler,ChannelPipeline,ChannelHandlerContext这些组件都有write,flush等方法,这些有什么区别呢
写一个测试项目,当前netty项目的出站方向有三个ChannelOutboundHandlerAdapter,添加pipeline时的方式如下
socketChannel.pipeline()
.addLast(new OutHandler2())
.addLast(new OutHandler1())
.addLast(new EchoClientChannelHandler())
.addLast(new OutHandler3())
;
OutHandler1的代码如下所示
System.out.println(((ByteBuf)msg).toString(CharsetUtil.UTF_8));
System.out.println("~~~~~~~~~ out handler1 process ~~~~~~~");
ctx.writeAndFlush(Unpooled.copiedBuffer("handler1 change message", CharsetUtil.UTF_8));
OutHandler2,3的代码如下所示
System.out.println(((ByteBuf)msg).toString(CharsetUtil.UTF_8));
System.out.println("~~~~~~~~~ out handler3 process ~~~~~~~");
ctx.writeAndFlush(msg);
这时在EchoClientChannelHandler中使用ctx.channel().writeAnfFlush方法时,结果如下所示
Netty rocks!
~~~~~~~~~ out handler3 process ~~~~~~~
Netty rocks!
~~~~~~~~~ out handler1 process ~~~~~~~
handler1 change message
~~~~~~~~~ out handler2 process ~~~~~~~
若是改为在EchoClientChannelHandler中使用ctx.writeAndFlush方法,结构如下所示
Netty rocks!
~~~~~~~~~ out handler1 process ~~~~~~~
handler1 change message
~~~~~~~~~ out handler2 process ~~~~~~~
若是改为在EchoClientChannelHandler中使用ctx.channel().pipeline().writeAndFlush方法时,结果如下所示
Netty rocks!
~~~~~~~~~ out handler3 process ~~~~~~~
Netty rocks!
~~~~~~~~~ out handler1 process ~~~~~~~
handler1 change message
~~~~~~~~~ out handler2 process ~~~~~~~
若是EchoClientChannelHandler中直接调用某个ChannelHandler的write方法,如下所示
ctx.pipeline().get(OutHandler1.class)
.write(ctx, Unpooled.copiedBuffer("Netty rocks!", CharsetUtil.UTF_8), new VoidChannelPromise(ctx.channel(),true));
结果如下所示
Netty rocks!
~~~~~~~~~ out handler1 process ~~~~~~~
handler1 change message
~~~~~~~~~ out handler1 process ~~~~~~~
handler1 change message
~~~~~~~~~ out handler2 process ~~~~~~~
若是改为在EchoClientChannelHandler中调用某个context的write方法,如下所示
ctx.pipeline().context(OutHandler1.class).writeAndFlush(Unpooled.copiedBuffer("Netty rocks!", CharsetUtil.UTF_8));
结果如下所示
Netty rocks!
~~~~~~~~~ out handler2 process ~~~~~~~
EchoClient receives: Netty rocks!
基于上面的测试结果,联系netty in action 中讲的
如果调用Channel或者ChannelPipeline上的这些方法,他们将沿着整个ChannelPipeline进行传播。而调用位于ChannelHandlerContext上的相同方法,则将从当前所关联的ChannelHandler开始,并且只会传播给位于该ChannelPipeline中的下一个能够处理该事件的ChannelHandler。
这就可以解释上面使用channel,pipeline,ctx这些组件write时的输出结果了。
要想调用从某个特定的ChannelHandler开始的处理过程,必须获取到在该ChannelHandler之前的ChannelHandler所关联的ChannelHandlerContext。这个ChannelHandlerContext将调用和它所关联的ChannelHandler之后的ChannelHandler。
消息将从下一个ChannelHandler开始流经ChannelPipeline,绕过所有前面的ChannelHandler。
这就解释了上面在直接获取OutHandler1的context后,消息直接从OutHandler2开始处理。
但是直接调用ChannelHandler的结果还是不好解释。
将OutHandler1的write方法改为
ctx.pipeline().context(OutHandler2.class).writeAndFlush(Unpooled.copiedBuffer("handler1 change message", CharsetUtil.UTF_8));
结果变为
Netty rocks!
~~~~~~~~~ out handler1 process ~~~~~~~
可以看到直接调用ChannelHandler时类似直接的方法调用,相当于直接在当前的HandlerConext中执行调用的ChannelHandler方法,方向的流转取决于调用方。
- 若调用的ChannelHandler中没有改变消息的流向,即直接ctx.write,那么就是普通的context间的消息流转。由于出站方向是注册Handler时的反方向,且OutHandler1注册在EchoClientChannelHandler前面,所以会产生了两次OutHandler1的调用。
- 若调用的ChannelHandler中改变了消息的流向,即调用了channel或者pipeline或者其他context的write方法,那么消息就按照调用方的顺序来流转了。由于OutHandler2在注册时的头部,所以在修改OutHandler1的方法后消息直接走到了pipeline的末尾。
网友评论