netty 之 入站、出站、handler顺序

netty 的所有handler构建通常是通过引到类创建的时候带进去的。代码一般如下:

//创建引导
EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workGroup = new NioEventLoopGroup(10);
        //
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workGroup).channel(NioServerSocketChannel.class)
                .childHandler(new NettyServerInitializer());

//实现具体的handler
public class NettyServerInitializer extends ChannelInitializer {
    @Override
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        ChannelPipeline pipeline = socketChannel.pipeline();
        //
        pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,
                0, 4, 0, 4));
        pipeline.addLast(new LengthFieldPrepender(4));
        pipeline.addLast(new StringDecoder(CharsetUtil.UTF_8));
        pipeline.addLast(new StringEncoder(CharsetUtil.UTF_8));
        //
        pipeline.addLast(new MyServerHandler());
        pipeline.addLast(new MyServerHandler2());
        log.info("add new channl {}", socketChannel);
    }
}

上面的代码可以看到 ,处理我们的业务过程中,需要很多handler来处理各种不同场景的需求。包括编码解码,处理io,处理业务等。那么问题来了: 通过addLast()加到处理链路的handler到底是什么执行顺序?
首先netty分为入站和出站处理器,通过继承不同的接口来区分。
内部通过DefaultChannelPipeline来维护这个链表。我们看下addLast代码

//来自DefaultChannelPipeline
private void addLast0(AbstractChannelHandlerContext newCtx) {
        AbstractChannelHandlerContext prev = tail.prev;
        newCtx.prev = prev;
        newCtx.next = tail;
        prev.next = newCtx;
        tail.prev = newCtx;
    }

可以看出来,所有的入站核出站的handler是维护在一个链表中。并且这个context默认已经由两个默认hander:headContext和tailContext
下图中,使用紫色代表入站Context,橙色代表出站Context。


image.png

这个图代表:
Tail是出站执行流程的启动点,但是,它最后一个入站处理器。
Hearder,是入站流程的启动起点,但是,它最后一个出站处理器。
也就是说:
处理入站事件 从head找到tail。属于顺序查找
处理出站事件 从tail找到head。属于倒序查找
另外: 执行入站事件(比如读事件),会从AbstractChannelHandlerContext#findContextInbound()查找所有的事件。而出站事件这是走的findContextOutbound.两个方法代码如下所示:

//入站和出站查找
 private AbstractChannelHandlerContext findContextInbound() {
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.next;
        } while (!ctx.inbound);
        return ctx;
    }

    private AbstractChannelHandlerContext findContextOutbound() {
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.prev;
        } while (!ctx.outbound);
        return ctx;
    }

可以看到。channel自身所关联的content上下文已经维护了一个双向链表。
在查询入站事件的时候一直查找next,查找出站事件的时候,一直查找prev

总结

入站: 从header到tail ,顺序。 初始化的时候addLast a,b,c三个handler。则执行顺序是 a,b,c
出站: 从tail找到header,倒序。初始化的时候addLast d,e,f 三个handelr。则执行顺序是f,e,d

你可能感兴趣的:(netty 之 入站、出站、handler顺序)