Netty 源码分析之ChannelPipeline 以4.0.15 final版

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Java_Jsp_Ssh/article/details/82779754

一:Channel 与 ChannelPipeline

        每个Channel在初始化时都会在其父类构造器中顺带构建一个ChannelPipeline,unSafe对象;即每个channel都有一个与之关联的channelPipeline;而channelPipeline是channelHandler承载的事件流容器,底层是以DefaultChannelHandlerContext为节点的双向链表;

而channelPipeline在初始化的时候就本身自带两个节点:head,tail;两个节点中的handler分别实现了ChannelOutBoundHandler,ChannelInBoundHandler;channel初始化时调用父类构造器如下:

protected AbstractChannel(Channel parent) {
    this.parent = parent;
    unsafe = newUnsafe();
    pipeline = new DefaultChannelPipeline(this);
}

AbstractChannel 有一个 pipeline 字段, 在构造器中会初始化它为 DefaultChannelPipeline的实例. 这里的代码就印证了一点: 每个 Channel 都有一个 ChannelPipeline.
接着我们跟踪一下 DefaultChannelPipeline 的初始化过程.
首先进入到 DefaultChannelPipeline 构造器中:

public DefaultChannelPipeline(AbstractChannel channel) {
        if (channel == null) {
            throw new NullPointerException("channel");
        }
        this.channel = channel;

        TailHandler tailHandler = new TailHandler();
        tail = new DefaultChannelHandlerContext(this, null, generateName(tailHandler), tailHandler);

        HeadHandler headHandler = new HeadHandler(channel.unsafe());
        head = new DefaultChannelHandlerContext(this, null, generateName(headHandler), headHandler);

        head.next = tail;
        tail.prev = head;
    }

DefaultChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutorGroup group, String name,
            ChannelHandler handler) {

        if (name == null) {
            throw new NullPointerException("name");
        }
        if (handler == null) {
            throw new NullPointerException("handler");
        }

        channel = pipeline.channel;
        this.pipeline = pipeline;
        this.name = name;
        this.handler = handler;

        if (group != null) {
            // Pin one of the child executors once and remember it so that the same child executor
            // is used to fire events for the same channel.
            EventExecutor childExecutor = pipeline.childExecutors.get(group);
            if (childExecutor == null) {
                childExecutor = group.next();
                pipeline.childExecutors.put(group, childExecutor);
            }
            executor = childExecutor;
        } else {
            executor = null;
        }

        inbound = handler instanceof ChannelInboundHandler;
        outbound = handler instanceof ChannelOutboundHandler;
    }

这里的TailHandler 与 headHandler 分别实现ChannelInboundHandler,ChannelOutboundHandler接口;然后通过DefaultChannelHandlerContext进行封装成两个对应的链表节点,相互指向形成双链表;这里4.1.33版本里的每个head,tail即使channelHandler,又是handlerContext,只是用一个类涵盖完了。同时DefaultChannelHandlerContext节点中的两个boolean值inbound ,outBound 是对类型的判断结果,这个两个布尔值是pipeline中handler事件流转的关键,而pipeline中事件总是从outBound流向inbound;

二:自定义ChannelInitializer 的添加

上节初始化的pipeline只是两个内部实例的channelHandler,并没有用户自定的handler,回到代码最初,看一下,自定义的handler如何添加到pipeline中;

Bootstrap b = new Bootstrap();
b.group(group)
 .channel(NioSocketChannel.class)
 .option(ChannelOption.TCP_NODELAY, true)
 .handler(new ChannelInitializer<SocketChannel>() {
     @Override
     public void initChannel(SocketChannel ch) throws Exception {
         ChannelPipeline p = ch.pipeline();
         p.addLast(new EchoClientHandler());
     }
 });

上面代码的初始化过程, 相信大家都不陌生. 在调用 handler 时, 传入了 ChannelInitializer 对象, 它提供了一个 initChannel 方法供我们初始化 ChannelHandler. 那么这个初始化过程是怎样的呢? 下面我们就来揭开它的神秘面纱.首先看一下ChannleInitializer类结构;

ChannelInitializer 实现了 ChannelHandler, 而且还是inBound类型的,那么它是在什么时候添加到 ChannelPipeline 中的呢? 进行了一番搜索后, 我们发现它是在 Bootstrap.init 方法中添加到 ChannelPipeline 中的.
其代码如下:

final ChannelFuture initAndRegister() {
        final Channel channel = channelFactory().newChannel();
        try {
            init(channel);
        } catch (Throwable t) {
            channel.unsafe().closeForcibly();
            return channel.newFailedFuture(t);
        }

        ChannelFuture regFuture = group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }


void init(Channel channel) throws Exception {
        ChannelPipeline p = channel.pipeline();
        p.addLast(handler());

        final Map<ChannelOption<?>, Object> options = options();
        synchronized (options) {
            for (Entry<ChannelOption<?>, Object> e: options.entrySet()) {
                try {
                    if (!channel.config().setOption((ChannelOption<Object>) e.getKey(), e.getValue())) {
                        logger.warn("Unknown channel option: " + e);
                    }
                } catch (Throwable t) {
                    logger.warn("Failed to set a channel option: " + channel, t);
                }
            }
        }

        final Map<AttributeKey<?>, Object> attrs = attrs();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                channel.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
            }
        }
    }

上面的代码将 handler() 返回的 ChannelHandler 添加到 Pipeline 中, 而 handler() 返回的是handler 其实就是我们在初始化 Bootstrap 调用 handler 设置的 ChannelInitializer 实例, 因此这里就是将 ChannelInitializer 插入到了 Pipeline 的末端.
此时 Pipeline 的结构如下图所示:

由于ChannelInitializer是一个channelHandler,而链表的节点是DefaultChannelHandlerContext;因此该ChannelInitializer肯定会再次被封装;看代码:

public ChannelPipeline addLast(EventExecutorGroup group, final String name, ChannelHandler handler) {
        synchronized (this) {
            checkDuplicateName(name);

            DefaultChannelHandlerContext newCtx = new DefaultChannelHandlerContext(this, group, name, handler);
            addLast0(name, newCtx);
        }

        return this;
    }

private void addLast0(final String name, DefaultChannelHandlerContext newCtx) {
        checkMultiplicity(newCtx);

        DefaultChannelHandlerContext prev = tail.prev;
        newCtx.prev = prev;
        newCtx.next = tail;
        prev.next = newCtx;
        tail.prev = newCtx;

        name2ctx.put(name, newCtx);

        callHandlerAdded(newCtx);
    }

由于ChannelInitializer是实现ChannelInboundHandler接口的,因此封装的DefaultChannelHandlerContext中inbound布尔值为true;

二:2  自定义 ChannelHandler 的添加过程

  上节只是将CannelInitializer添加到pipeline中,而其初始化方法中的自定义handler合适被添加,这里只需查找CannelInitializer中initChannel合适被调用即可。首先对

在上一小节中, 我们已经分析了一个 ChannelInitializer 如何插入到 Pipeline 中的, 接下来就来探讨一下 ChannelInitializer 在哪里被调用, ChannelInitializer 的作用, 以及我们自定义的 ChannelHandler 是如何插入到 Pipeline 中的.客户端channel 的注册过简单地复习一下:

  • 首先在 AbstractBootstrap.initAndRegister中, 通过 group().register(channel), 调用 MultithreadEventLoopGroup.register 方法

  • 在MultithreadEventLoopGroup.register 中, 通过 next() 获取一个可用的 SingleThreadEventLoop, 然后调用它的 register

  • 在 SingleThreadEventLoop.register 中, 通过 channel.unsafe().register(this, promise) 来获取 channel 的 unsafe() 底层操作对象, 然后调用它的 register.

  • 在 AbstractUnsafe.register 方法中, 调用 register0 方法注册 Channel

  • 在 AbstractUnsafe.register0 中, 调用 AbstractNioChannel#doRegister 方法

  • AbstractNioChannel.doRegister 方法通过 javaChannel().register(eventLoop().selector, 0, this) 将 Channel 对应的 Java NIO SockerChannel 注册到一个 eventLoop 的 Selector 中, 并且将当前 Channel 作为 attachment.

而我们自定义 ChannelHandler 的添加过程, 发生在 AbstractUnsafe.register0 中, 在这个方法中调用了 pipeline.fireChannelRegistered() 方法, 其实现如下:

private void register0(ChannelPromise promise) {
            try {
                // check if the channel is still open as it could be closed in the mean time when the register
                // call was outside of the eventLoop
                if (!ensureOpen(promise)) {
                    return;
                }
                doRegister();
                registered = true;
                promise.setSuccess();
                pipeline.fireChannelRegistered();
                if (isActive()) {
                    pipeline.fireChannelActive();
                }
            } catch (Throwable t) {
                // Close the channel directly to avoid FD leak.
                closeForcibly();
                closeFuture.setClosed();
                if (!promise.tryFailure(t)) {
                    logger.warn(
                            "Tried to fail the registration promise, but it is complete already. " +
                                    "Swallowing the cause of the registration failure:", t);
                }
            }
        }



@Override
public ChannelPipeline fireChannelRegistered() {
    head.fireChannelRegistered();
    return this;
}

@Override
    public ChannelPipeline fireChannelRegistered() {
        head.fireChannelRegistered();
        return this;
    }

而head.fireChannlelRegistered方法调用其DefaultChannelHandlerContext节点里方法:

@Override
    public ChannelHandlerContext fireChannelRegistered() {
        final DefaultChannelHandlerContext next = findContextInbound();
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            next.invokeChannelRegistered();
        } else {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    next.invokeChannelRegistered();
                }
            });
        }
        return this;
    }

private DefaultChannelHandlerContext findContextInbound() {
        DefaultChannelHandlerContext ctx = this;
        do {
            ctx = ctx.next;
        } while (!ctx.inbound);
        return ctx;
    }

而在findContextInbound是在双链表中查找第一个布尔值inbound为true的节点;而上节我们添加channelInitializer就是第一节点;当获取到 inbound 的 Context 后, 就调用它的 invokeChannelRegistered 方法:

 private void invokeChannelRegistered() {
        try {
            ((ChannelInboundHandler) handler).channelRegistered(this);
        } catch (Throwable t) {
            notifyHandlerException(t);
        }
    }
invokeChannelRegistered方法中的handler就是封装了DefaultChannelHandlerContext节点中的ChannelInitializer;然后触发其channelRegistered方法;
public final void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        ChannelPipeline pipeline = ctx.pipeline();
        boolean success = false;
        try {
            initChannel((C) ctx.channel());
            pipeline.remove(this);
            ctx.fireChannelRegistered();
            success = true;
        } catch (Throwable t) {
            logger.warn("Failed to initialize a channel. Closing: " + ctx.channel(), t);
        } finally {
            if (pipeline.context(this) != null) {
                pipeline.remove(this);
            }
            if (!success) {
                ctx.close();
            }
        }
    }

而上述initChannel方法的实现就是我们客户端代码辅助类添加的handler时所实现的initChannel方法;这里传入的channel即辅助类启动时指明的NioStockChannel 或者serverNioStockChannel

.handler(new ChannelInitializer<Channel>() {
                        protected void initChannel(Channel ch) throws Exception {
                            ch.pipeline().addLast(channelHandler);

                        };
                    });

这里自定义的handler添加完毕后,会将ChannelInitializer 这个ChannelHandler从pipeline中移除掉;

因此当调用了这个方法后, 我们自定义的 ChannelHandler 就插入到 Pipeline 了, 此时的 Pipeline 如下图所示:

当添加了自定义的 ChannelHandler 后, 会删除 ChannelInitializer 这个 ChannelHandler, 即 "ctx.pipeline().remove(this)", 因此最后的 Pipeline 如下:

总结:每个channel基本都会经历以下阶段
     (1):首先经过辅助类进行各种初始化;

      a: ChannelFactory  工厂实现类的初始化; channel 是通过ChannelFactory 基于工厂模式,底层经过反射初始化而成;由于只有在启动时才会对Channel进行构建,也就反射一次,可以忽略性能影响;

     b:Reacto模式线程池初始化;c:heanler类的初始化,这里的handler类型是netty内部的handler类,非自定义的;

  (2)channel的注册,初始化,添加自定handler等这些操作,都是在辅助类在启动bind方法或者connect方法时才开始执行的

     a:初始化channel,通过channel工厂newInstance方法,构建指定的Chanler

    b:初始化channelPipeline,构建双链表结构的pipeline容器;

   c:初始化unsafe类,这个接口是实现与底层stock通信的功能类;

  d:添加自定义ChannelHander

  f: 将channel与EventLoop进行绑定,即注册到selectot模型中;然后触发各种IO事件;

猜你喜欢

转载自blog.csdn.net/Java_Jsp_Ssh/article/details/82779754