最近在做netty 部标开发,顺便研究了下pipeline执行顺序,发现中文博客,网站讲的都不太透彻。
自己结合网上例子,实验,再最后看源码,总结下,希望能讲的透彻。
借用网上博客一个例子https://blog.csdn.net/u013252773/article/details/21195593 : 如添加pipelinehandler顺序是:
pipeline.addLast(new InboundsHandler1()); //in1
pipeline.addLast(new InboundsHandler2()); //in2
pipeline.addLast(new outboundsHandler1()); //out1
pipeline.addLast(new outboundsHandler2()); //out2
各个handler内容如下:
public class InboundHandler1 extends ChannelInboundHandlerAdapter {
private static Logger logger = LoggerFactory.getLogger(InboundHandler1.class);
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
logger.info("InboundHandler1.channelRead: ctx :" + ctx);
// 通知执行下一个InboundHandler
ctx.fireChannelRead(msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
logger.info("InboundHandler1.channelReadComplete");
ctx.flush();
}
}
public class InboundHandler2 extends ChannelInboundHandlerAdapter {
private static Logger logger = LoggerFactory.getLogger(InboundHandler2.class);
@Override
// 读取Client发送的信息,并打印出来
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
logger.info("InboundHandler2.channelRead: ctx :" + ctx);
ByteBuf result = (ByteBuf) msg;
byte[] result1 = new byte[result.readableBytes()];
result.readBytes(result1);
String resultStr = new String(result1);
System.out.println("Client said:" + resultStr);
result.release();
ctx.write(msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
logger.info("InboundHandler2.channelReadComplete");
ctx.flush();
}
}
public class OutboundHandler1 extends ChannelOutboundHandlerAdapter {
private static Logger logger = LoggerFactory.getLogger(OutboundHandler1.class);
@Override
// 向client发送消息
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
logger.info("OutboundHandler1.write");
String response = "I am ok!";
ByteBuf encoded = ctx.alloc().buffer(4 * response.length());
encoded.writeBytes(response.getBytes());
ctx.write(encoded);
ctx.flush();
}
}
public class OutboundHandler2 extends ChannelOutboundHandlerAdapter {
private static Logger logger = LoggerFactory.getLogger(OutboundHandler2.class);
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
logger.info("OutboundHandler2.write");
// 执行下一个OutboundHandler
super.write(ctx, msg, promise);
}
}
在netty 的 DefaultChannelPipeline中是用了双向链表放置 ,这些 handler不管是什么handler, Inbound, outBound, Duplex双向的,都是放置在AbstractChannelHandlerContext的双向链表(DefaultChannelPipeline持有head, tail).
handler添加顺序就是链表里顺序
(head)InboundsHandler1()<--->InboundsHandler2()<--->outboundsHandler1()<--->outboundsHandler2() (tail)
pipeline有read到数据后,是从head往后查找 有in性质的handler,
pipeline有write数据是,是从 tail往前查找 有out性质的handler
以write为例, 通过源码,DefaultChannelPipeline:
@Override
public final ChannelFuture write(Object msg) {
return tail.write(msg);
}
AbstractChannelHandlerContext:
private void write(Object msg, boolean flush, ChannelPromise promise) {
ObjectUtil.checkNotNull(msg, "msg");
try {
if (isNotValidPromise(promise, true)) {
ReferenceCountUtil.release(msg);
// cancelled
return;
}
} catch (RuntimeException e) {
ReferenceCountUtil.release(msg);
throw e;
}
final AbstractChannelHandlerContext next = findContextOutbound(flush ?
(MASK_WRITE | MASK_FLUSH) : MASK_WRITE);
........
}
AbstractChannelHandlerContext :
private AbstractChannelHandlerContext findContextInbound(int mask) {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.next;
} while ((ctx.executionMask & mask) == 0);
return ctx;
}
private AbstractChannelHandlerContext findContextOutbound(int mask) {
AbstractChannelHandlerContext ctx = this;
do {
ctx = ctx.prev;
} while ((ctx.executionMask & mask) == 0);
return ctx;
}
通过DefaultPipeline源码可以看出
pipeline.addLast(new InboundsHandler1()); //in1
pipeline.addLast(new InboundsHandler2()); //in2
pipeline.addLast(new outboundsHandler1()); //out1
pipeline.addLast(new outboundsHandler2()); //out2
这么添加的顺序, 在InboundsHandler2里发送消息, 用 ctx.write(msg) 是不走outboundsHandler1和outboundsHandler2的
因为ctx.writeAndFlush()和ctx.channel().writeAndFlush()是有区别的;ctx.writeAndFlush()从当前节点往前查找out性质的handler,而out节点注册在当前节点后边,所以查找不到,当然没走outHandler了。调用ctx.channel().writeAndFlush()从链表结尾开始往前查找out性质的handler,可以InboundsHandler2里发送改成此种方式试试。
可以把添加2个InboundsHandler和2个outboundsHandler随便调换位置,分别使用ctx.writeAndFlush()和ctx.channel().writeAndFlush() 发送,帮助理解