Netty ChannelPipeline使用详解

ChannelPipeline的设计思想

ChannelPipeline是一个双向链表,Channel都有且仅有一个ChannelPipeline与之对应,Channel包含了ChannelPipeline,ChannelPipeline内部包含了N个channelhandler,每一个handler都是由一个线程去执行。
用户可在pipeline中添加多个事件处理器(ChannelHandler),并通过实现ChannelHandler中定义的方法,对回调事件进行定制化的业务处理。ChannelHandler也可以调用自身方法对Channel本身进行操作。

ChannelPipeline的创建

ChannelPipeline的创建是在AbstractChannel的构造方法中创建的,这就意味着每一个AbstractChannel的子类在创建的时候都会创建一个ChannelPipeline,AbstractChannel子类,如下图所示:
在这里插入图片描述
类图是删减版的只讲解核心主线,平时常用的就是NioServerSocketChannel,这也说明在ChannelPipeline的创建是在创建NioServerSocketChannel的时候创建的。
对应的源码如下:

  protected AbstractChannel(Channel parent) {
        this.parent = parent;
        id = newId();
        unsafe = newUnsafe();
        pipeline = newChannelPipeline();
    }
ChannelPipeline的实例化

继续跟踪代码查看ChannelPipeline创建的时候是如何实例化的,源码如下:

//相关变量
    final AbstractChannelHandlerContext head;
    final AbstractChannelHandlerContext tail;

  protected DefaultChannelPipeline(Channel channel) {
        this.channel = ObjectUtil.checkNotNull(channel, "channel");
        succeededFuture = new SucceededChannelFuture(channel, null);
        voidPromise =  new VoidChannelPromise(channel, true);
		//分别构建Pipeline的头对象和尾对象,TailContext和HeadContext都是AbstractChannelHandlerContext,这里不多做解释
        tail = new TailContext(this);
        head = new HeadContext(this);
		//形成双向链表
        head.next = tail;
        tail.prev = head;
    }

这里需要简单说明一下TailContext和HeadContext的区别,TailContext和HeadContext都继承了AbstractChannelHandlerContext,但是TailContext只实现了ChannelInboundHandler接口,但是HeadContext实现了ChannelInboundHandler和ChannelOutboundHandler接口,
head节点作为pipeline的头结点开始接收并传递inbound事件。并作为pipeline的最后一环最终接收处理outbound事件(委托Unsafe进行outbound事件的相关IO操作)。
tail节点作为pipeline的第一环传递outbound事件,其实就是将outbound事件透传到前一个outbound处理节点。并作为pipeline的最后一环最终接收inbound事件,大部分左右是终止inbound事件的传播。

ChannelInitializer 的添加

到目前为止,这个 Pipeline 还并不能实现什么特殊的功能,因为head和tail并没有实际的功能,通常来说,我们在初始化 ServerBootstrap,会添加我们自定义的 ChannelHandler, 以服务端启动代码片段来举例:

  bootstrap.group(bossGroup, workerGroup) //设置两个线程组
                    // 使用NioServerSocketChannel作为服务器的通道实现
                    .channel(NioServerSocketChannel.class)
                    // 初始化服务器连接队列大小,服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接。
                    // 多个客户端同时来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new ChannelInitializer<SocketChannel>() {//创建通道初始化对象,设置初始化参数,在 SocketChannel 建立起来之前执行

                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //对workerGroup的SocketChannel设置处理器
                            ch.pipeline().addLast(new NettyServerHandler());
                        }
                    });

在调用 handler 时,传入了一个 ChannelInitializer 对象,它提供了一个 initChannel()方法给我们初始化 ChannelHandler,init执行如下所示:

 void init(Channel channel) throws Exception {
        final Map<ChannelOption<?>, Object> options = options0();
        synchronized (options) {
            setChannelOptions(channel, options, logger);
        }

        final Map<AttributeKey<?>, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
                channel.attr(key).set(e.getValue());
            }
        }
		//从ChannelPipeline 中获取ChannelPipeline 
        ChannelPipeline p = channel.pipeline();

        final EventLoopGroup currentChildGroup = childGroup;
        final ChannelHandler currentChildHandler = childHandler;
        final Entry<ChannelOption<?>, Object>[] currentChildOptions;
        final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
        synchronized (childOptions) {
            currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0));
        }
        synchronized (childAttrs) {
            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
        }
		//先添加配置相关的ChannelHandler 
        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) throws Exception {
                final ChannelPipeline pipeline = ch.pipeline();
                ChannelHandler handler = config.handler();
                if (handler != null) {
                //添加handler
                    pipeline.addLast(handler);
                }
				//执行childHandler的init方法,把自定义的handle添加到ChannelPipeline 中
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }

到这里ChannelInitializer 执行就算完成了, config.handler()返回的 ChannelHandler 添加到 Pipeline 中,而 handler()返回的其实就是我们在初始化 Bootstrap 时通过 handler()方法设置的 ChannelInitializer 实例(ChannelInitializer 实现了 ChannelInboundHandlerAdapter),因此这里其实就是将 ChannelInitializer 插入到了 Pipeline 的末端,到这些说明了为什么我们需要先配置ChannelInitializer ,和ChannelInitializer 中initChannel的主要功能。

ChannelHandler的调用原理

ChannelHandler调用是在ServerBootstrap.bind()调用的时候,执行initAndRegister();层层调用到AbstractChannel.AbstractUnsafe#register0方法,源码如下:

//第一步
//AbstractChannel.AbstractUnsafe#register0
private void register0(ChannelPromise promise) {
	doRegister();//将SocketChannel注册到selector中
    pipeline.fireChannelRegistered();//重点分析
}
//第二步
//DefaultChannelPipeline#fireChannelRegistered
public final ChannelPipeline fireChannelRegistered() {
    AbstractChannelHandlerContext.invokeChannelRegistered(head);//传入head,开始执行
    return this;
}
//第三步
    static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
        EventExecutor executor = next.executor();
        //判断时候在EventLoop()中,如果没有开启线程异步执行
        if (executor.inEventLoop()) {
            next.invokeChannelRegistered();
        } else {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    next.invokeChannelRegistered();
                }
            });
        }
    }
    //第四步
    private void invokeChannelRegistered() {
        if (invokeHandler()) {
            try {
                ((ChannelInboundHandler) handler()).channelRegistered(this);
            } catch (Throwable t) {
                notifyHandlerException(t);
            }
        } else {
            fireChannelRegistered();
        }
    }
    //第五步
    private void invokeChannelRegistered() {
        if (invokeHandler()) {
            try {
                ((ChannelInboundHandler) handler()).channelRegistered(this);
            } catch (Throwable t) {
                notifyHandlerException(t);
            }
        } else {
            fireChannelRegistered();
        }
    }
    //第六步
       @Skip
    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        ctx.fireChannelRegistered();
    }
    //第七步
  @Override
    public ChannelHandlerContext fireChannelRegistered() {
        invokeChannelRegistered(findContextInbound(MASK_CHANNEL_REGISTERED));
        return this;
    }

再继续执行就会进入循环,一直执行第三步->第七步。而退出逻辑在第七步中的
findContextInbound(MASK_CHANNEL_REGISTERED)方法,源码如下:

  private AbstractChannelHandlerContext findContextInbound(int mask) {
        AbstractChannelHandlerContext ctx = this;
        do {
            ctx = ctx.next;
            //判断ctx的执行标志是不是Inbound,是的话结束循环,依次执行所有的ChannelHandler的channelRegistered方法
        } while ((ctx.executionMask & mask) == 0);
        return ctx;
    }

这里需要注意得是head和tail的channelRegistered的实现,如下所示:

	//head的,直接调用了下一个channelHandle的ChannelRegistered();方法
        @OverridechannelRegistered,直接调用了下一个channelHandle的ChannelRegistered();方法
        public void channelRegistered(ChannelHandlerContext ctx) {
            invokeHandlerAddedIfNeeded();
            ctx.fireChannelRegistered();
        }
        //tail的,没有任何的执行代码,说明到这里整个链条执行完成。当然channelRegistered只会在bind()方法中的initAndRegistered()的时候调用
             @Override
        public void channelRegistered(ChannelHandlerContext ctx) { }

head的,直接调用了下一个channelHandle的ChannelRegistered();方法
tail的,没有任何的执行代码,说明到这里整个链条执行完成。当然channelRegistered只会在bind()方法中的initAndRegistered()的时候调用。
PS:在ChannelHandler执行的时候,无论是任何的IO操作,如果想要链式执行必须调用对应的ctx.fireChannelXX()方法,不管是channelRegistered,还是channelActive,或者channelRead,否则都会在当前ChannelHandler中结束,不在执行下一个ChannelHandler对应的方法。

ChannelHandler添加原理

上面的流程我们知道ChannelHandler执行的时候,会从 head 开始遍历 Pipeline 的双向链表,然后找到第一个属性 inbound 为 true 的 ChannelHandlerContext 实例,而我们代码中配置完成之后,只有一个 ChannelInboudHandler,就是ChannelInitializer ,在ChannelInitializer 中的hannelRegistered()方法没有执行之前,Pipeline的里面的链表内容如下:
在这里插入图片描述
ChannelInitializer的注册方法如下:

    public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        // Normally this method will never be called as handlerAdded(...) should call initChannel(...) and remove
        // the handler.
        //核心方法
        if (initChannel(ctx)) {
            // we called initChannel(...) so we need to call now pipeline.fireChannelRegistered() to ensure we not
            // miss an event.
            ctx.pipeline().fireChannelRegistered();

            // We are done with init the Channel, removing all the state for the Channel now.
            removeState(ctx);
        } else {
            // Called initChannel(...) before which is the expected behavior, so just forward the event.
            ctx.fireChannelRegistered();
        }
    }
    private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
        if (initMap.add(ctx)) { // Guard against re-entrance.
            try {
            //执行重写的initChannel方法
                initChannel((C) ctx.channel());
            } catch (Throwable cause) {
                // Explicitly call exceptionCaught(...) as we removed the handler before calling initChannel(...).
                // We do so to prevent multiple calls to initChannel(...).
                exceptionCaught(ctx, cause);
            } finally {
                ChannelPipeline pipeline = ctx.pipeline();
                if (pipeline.context(this) != null) {
                    pipeline.remove(this);
                }
            }
            return true;
        }
        return false;
    }

注意initChannel()方法中调用了initChannel(© ctx.channel()),这个initChannel(C ch)方法我们也很熟悉,它就是我们在初始化 Bootstrap 时,调用 handler 方法传入的匿 名内部类所实现的方法:

.childHandler(new ChannelInitializer<SocketChannel>() {//创建通道初始化对象,设置初始化参数,在 SocketChannel 建立起来之前执行

                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            //对workerGroup的SocketChannel设置处理器
                            ch.pipeline().addLast(new NettyServerHandler());
                        }
                    });

当我们调用完这个方法之后, 我们自定义的 ChannelHandler 就插入到了 Pipeline,此时 Pipeline 的状态如下图所示:
在这里插入图片描述
当添加完成自定义的 ChannelHandler 后,在 finally 代码块会删除自定义的 ChannelInitializer,也就是 remove(ctx)最 终调用 ctx.pipeline().remove(this),因此最后的 Pipeline 的状态如下:
在这里插入图片描述

Pipeline中的addLast方法
    public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;
        synchronized (this) {
            checkMultiplicity(handler);

            newCtx = newContext(group, filterName(name, handler), handler);
			//添加ChannelHandler 到链表中
            addLast0(newCtx);

            // If the registered is false it means that the channel was not registered on an eventLoop yet.
            // In this case we add the context to the pipeline and add a task that will call
            // ChannelHandler.handlerAdded(...) once the channel is registered.
            if (!registered) {
                newCtx.setAddPending();
                callHandlerCallbackLater(newCtx, true);
                return this;
            }

            EventExecutor executor = newCtx.executor();
            if (!executor.inEventLoop()) {
                callHandlerAddedInEventLoop(newCtx, executor);
                return this;
            }
        }
        callHandlerAdded0(newCtx);
        return this;
    }
    //分析这段代码可以知道addLast每次都把新的channelhandle添加到tail的前面
    private void addLast0(AbstractChannelHandlerContext newCtx) {
        AbstractChannelHandlerContext prev = tail.prev;
        newCtx.prev = prev;
        newCtx.next = tail;
        prev.next = newCtx;
        tail.prev = newCtx;
    }
Pipeline 的事件分类

1、Inbound事件
用于描述因外部事件导致的Channel状态变更。
在这里插入图片描述
2、Outbound事件
用于定义Channel能够提供的IO操作。
在这里插入图片描述

Pipeline的事件处理机制
Unsafe介绍

Unsafe是Channel的内部类,一个Channel对应一个Unsafe。

Unsafe用于处理Channel对应网络IO的底层操作。ChannelHandler处理回调事件时产生的相关网络IO操作最终也会委托给Unsafe执行。
在Netty中socket的相关操作,包括SocketAddress获取、selector注册、网卡端口绑定、socket建连与断连、socket写数据。这些操作都是有Unsafe来实现完成的,之后Unsafe在调用对应的 pipeline.fireXXX();方法进行事件的处理。
在这里插入图片描述
NioByteUnsafe实现了与socket连接的字节数据读取相关的操作。
NioMessageUnsafe实现了与新连接建立相关的操作。
在这里插入图片描述

inbound事件处理

在这里插入图片描述
fireChannelRead的执行流程如上图所示,fireChannelActiv、fireChannelReadComplete等inbound事件的处理过程可以自己跟代码看一下。(head节点对channelRead事件向后透传。对channelActive和channelReadComplete事件,向后传递并产生了Channel.read()的outbound事件)。
outbound事件处理的事件处理针对的是bind、connect、read、writeAndFlush等outbound事件的处理过程,这里不做详解,但是这些操作最后都是委托给DefaultChannelPipeline中的HeadContext的IO操作方法的方法,源码如下:

@Override
        public void bind(
                ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) {
            unsafe.bind(localAddress, promise);
        }

        @Override
        public void connect(
                ChannelHandlerContext ctx,
                SocketAddress remoteAddress, SocketAddress localAddress,
                ChannelPromise promise) {
            unsafe.connect(remoteAddress, localAddress, promise);
        }

        @Override
        public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) {
            unsafe.disconnect(promise);
        }

        @Override
        public void close(ChannelHandlerContext ctx, ChannelPromise promise) {
            unsafe.close(promise);
        }
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值