netty pipeline 执行顺序解读

最近在做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() 发送,帮助理解

 

 

你可能感兴趣的:(Java,netty)