ChannelPipeline源码剖析

本文深入解析Netty中ChannelPipeline的工作原理,包括ChannelHandlerContext的作用、ChannelHandler的添加与触发流程、事件传播机制等内容。

转载自:https://blog.youkuaiyun.com/u010853261/article/details/62218715
    http://blog.youkuaiyun.com/z69183787/article/details/52623501

  在netty中,每个Channel 有且仅有一个ChannelPipeline与之对应,他们的组成关系如下图所示:

  每一个Channel都与一个ChannelPipeline对应。ChannelPipeline实际上应该叫做ChannelHandlerPipeline,在ChannelPipeline中又维护了一个由ChannelHandlerContext组成的双向链表。这个链表的头是HeadContext, 链表的尾是TailContext, 并且每个ChannelHandlerContext又与一个 ChannelHandler一一对应。当需要对Channel进行某种处理的时候,Pipeline负责依次调用每一个Handler进行处理。
  每个Channel都有一个属于自己的Pipeline,调用Channel#pipeline()方法可以获得Channel的Pipeline,调用Pipeline#channel()方法可以获得Pipeline关联的Channel。
  在分析netty的启动引导类源码的时候,在执行bind()函数时会对channel进行初始化并注册,其中进行到后面会调用基类AbstractChannel的构造函数:

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

  AbstractChannel 有一个 pipeline 字段, 在构造器中会初始化它为DefaultChannelPipeline的实例。ChannelPipeline并不是直接管理ChannelHandler,而是通过ChannelHandlerContext来间接管理。

/**
 * The default {@link ChannelPipeline} implementation.  It is usually created
 * by a {@link Channel} implementation when the {@link Channel} is created.
 */
public class DefaultChannelPipeline implements ChannelPipeline {

    //…略
    final AbstractChannelHandlerContext head;
    final AbstractChannelHandlerContext tail;

    private final Channel channel;

    //…略
protected DefaultChannelPipeline(Channel channel) {
        this.channel = ObjectUtil.checkNotNull(channel, "channel");
        succeededFuture = new SucceededChannelFuture(channel, null);
        voidPromise =  new VoidChannelPromise(channel, true);

        tail = new TailContext(this);
        head = new HeadContext(this);

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

    //…略
}

  构造器中, 首先将与DefaultChannelPipeline关联的Channel保存到字段channel中, 然后实例化两个ChannelHandlerContext,一个是HeadContext实例head,另一个是TailContext实例tail。接着将head和tail互相指向,构成一个双向链表。

  AbstractChannelHandlerContext的定义如下:

abstract class AbstractChannelHandlerContext extends DefaultAttributeMap
        implements ChannelHandlerContext, ResourceLeakHint {

    //…略
    volatile AbstractChannelHandlerContext next;
    volatile AbstractChannelHandlerContext prev;

    private final boolean inbound;
    private final boolean outbound;
    private final DefaultChannelPipeline pipeline;
    private final String name;
    private final boolean ordered;

    //…略

  TailContext实现了ChannelInboundHandler 接口,作为入站事件的起点。HeadContext作为出站事件的起点。并且head和tail都实现了 ChannelHandlerContext 接口, 因此可以说 head 和 tail 既是一个 ChannelHandler, 又是一个 ChannelHandlerContext。
  Channel、ChannelPipeline、ChannelHandlerContext会形成如下关系:

  在ChinnelPipeline内部,ChannelHandlerContext组成了一个双向链表,ChinnelPipeline内部会使用了head和tail变量来标志头和尾。

  那么最开始的时候 ChannelPipeline中含有两个ChannelHandlerContext(同时也是 ChannelHandler),Netty是什么时候把自定义的 ChannelHandler添加到ChannelPipeline中呢?

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());
     }
 });

  ChannelInitializer 实现了 ChannelHandler, 那么它是在什么时候添加到 ChannelPipeline 中的呢?在Bootstrap.init()方法中:

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

    //…略
}

  上面的代码将handler()返回的ChannelHandler添加到Pipeline中,而handler()返回的handler其实就是我们在调用handler()方法时设置的ChannelInitializer实例,因此这里就是将ChannelInitializer插入到了 Pipeline的末端.

@Override
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
    final AbstractChannelHandlerContext newCtx;
    synchronized (this) {
        checkMultiplicity(handler);

        newCtx = newContext(group, filterName(name, handler), handler);

        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;
        }

        //...略
    }
    //...略
}

  ChannelInitializer是一个InboundHandler,netty会把它包装成DefaultChannelHandlerContext,最终调用addLast0()插入到pipeline的结点,插入的位置就是双链表的尾部,其实是tail尾结点的前面:

private void addLast0(AbstractChannelHandlerContext newCtx) {
    AbstractChannelHandlerContext prev = tail.prev;
    newCtx.prev = prev;
    newCtx.next = tail;
    prev.next = newCtx;
    tail.prev = newCtx;
}

  此时 Pipeline 的结构如下图所示:

  除了把newCtx加到ChannelPipeline中,还调用了callHandlerCallbackLater方法,将newCtx生成PendingHandlerAddedTask任务。


  已经分析了ChannelInitializer 如何插入到 Pipeline 中的, 接下来就来探讨一下 ChannelInitializer 的 initChannel()方法在哪里被调用?
  在分析Netty 源码 Bootstrap 的 channel 的注册过程中, 已经分析过 Channel 的注册过程了, 这里再简单地复习一下:

  1. 首先在AbstractBootstrap.initAndRegister中, 通过group().register(channel), 调用 MultithreadEventLoopGroup.register方法
  2. 在MultithreadEventLoopGroup.register中, 通过next()获取一个可用的SingleThreadEventLoop, 然后调用它的 register
  3. 在 SingleThreadEventLoop.register 中, 通过 channel.unsafe().register(this, promise) 来获取 channel 的 unsafe() 底层操作对象, 然后调用它的 register.
  4. 在 AbstractUnsafe.register 方法中, 调用 register0 方法注册 Channel
  5. 在 AbstractUnsafe.register0 中, 调用 AbstractNioChannel#doRegister 方法
  6. AbstractNioChannel.doRegister 方法通过 javaChannel().register(eventLoop().selector, 0, this) 将 Channel 对应的 Java NIO SockerChannel 注册到一个 eventLoop 的 Selector 中, 并且将当前 Channel 作为 attachment.

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

final void invokeHandlerAddedIfNeeded() {
    assert channel.eventLoop().inEventLoop();
    if (firstRegistration) {
        firstRegistration = false;
        // We are now registered to the EventLoop. It's time to call the callbacks for the ChannelHandlers,
        // that were added before the registration was done.
        callHandlerAddedForAllHandlers();
    }
}
private void callHandlerAddedForAllHandlers() {
    final PendingHandlerCallback pendingHandlerCallbackHead;
    synchronized (this) {
        assert !registered;

        // This Channel itself was registered.
        registered = true;

        pendingHandlerCallbackHead = this.pendingHandlerCallbackHead;
        // Null out so it can be GC'ed.
        this.pendingHandlerCallbackHead = null;
    }

    // This must happen outside of the synchronized(...) block as otherwise handlerAdded(...) may be called while
    // holding the lock and so produce a deadlock if handlerAdded(...) will try to add another handler from outside
    // the EventLoop.
    PendingHandlerCallback task = pendingHandlerCallbackHead;
    while (task != null) {
        task.execute();
        task = task.next;
    }
}

  还记得之前,将newCtx生成PendingHandlerAddedTask任务的流程,这里就开始执行了,会调用到DefaultChannelPipeline#callHandlerAdded0

private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) {
    try {
        ctx.handler().handlerAdded(ctx);
        ctx.setAddComplete();
    } catch (Throwable t) {
        //…略   
    }
}
@Override
public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
    if (ctx.channel().isRegistered()) {
        // This should always be true with our current DefaultChannelPipeline implementation.
        // The good thing about calling initChannel(...) in handlerAdded(...) is that there will be no ordering
        // surprises if a ChannelInitializer will add another ChannelInitializer. This is as all handlers
        // will be added in the expected order.
        initChannel(ctx);
    }
}
@SuppressWarnings("unchecked")
private boolean initChannel(ChannelHandlerContext ctx) throws Exception {
    if (initMap.putIfAbsent(ctx, Boolean.TRUE) == null) { // Guard against re-entrance.
        try {
            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 {
            remove(ctx);
        }
        return true;
    }
    return false;
}

  这样我们自定义的 ChannelHandler 就插入到 Pipeline 了, 当添加完自定义的 ChannelHandler 后, 会删除 ChannelInitializer 这个ChannelHandler, 即在initChannel(ctx)方法里面的finally块调用remove(this)


  那么事件如何在Pipeline里传播呢?从Channel的抽象子类AbstractChannel开始,下面是AbstractChannel#write()方法的实现:

    @Override
    public ChannelFuture write(Object msg) {
        return pipeline.write(msg);
    }

  AbstractChannel直接调用了Pipeline的write()方法,再看DefaultChannelPipeline的write()方法实现:

    @Override
    public final ChannelFuture write(Object msg) {
        return tail.write(msg);
    }

  因为write是个outbound事件,所以DefaultChannelPipeline直接找到tail指向的ChannelHandlerContext,调用其write()方法,接着看AbstractChannelHandlerContext的write()方法:

    @Override
    public ChannelFuture write(Object msg) {
        return write(msg, newPromise());
    }

    @Override
    public ChannelFuture write(final Object msg, final ChannelPromise promise) {
        if (msg == null) {
            throw new NullPointerException("msg");
        }

        try {
            if (isNotValidPromise(promise, true)) {
                ReferenceCountUtil.release(msg);
                // cancelled
                return promise;
            }
        } catch (RuntimeException e) {
            ReferenceCountUtil.release(msg);
            throw e;
        }
        write(msg, false, promise);

        return promise;
    }

    private void write(Object msg, boolean flush, ChannelPromise promise) {
        AbstractChannelHandlerContext next = findContextOutbound();
        final Object m = pipeline.touch(msg, next);
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            if (flush) {
                next.invokeWriteAndFlush(m, promise);
            } else {
                next.invokeWrite(m, promise);
            }
        } else {
            AbstractWriteTask task;
            if (flush) {
                task = WriteAndFlushTask.newInstance(next, m, promise);
            }  else {
                task = WriteTask.newInstance(next, m, promise);
            }
            safeExecute(executor, task, promise, m);
        }
}

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

  ChannelHandlerContext的write()方法沿着context链往前找,直至找到一个outbound类型的context为止,然后调用其invokeWrite()方法,最终调用ChannelOutboundHandler的write方法,即ChannelOutboundHandlerAdapter的write方法:

    @Override
    public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
        ctx.write(msg, promise);
    }

  这里会调用context链中前一个outbound的ChannelHandlerContext的write,然后重复上述流程,这样write事件就沿着outbound链继续传播。


  Netty中的inbound事件通常由I/O线程触发,例如TCP链路建立事件、链路关闭事件、读事件、异常通知事件等。
  触发Inbound事件的方法如下:

1)ChannelHandlerCOntext.fireChannelRegistered():Channel注册事件;
2)ChannelHandlerContext.fireChannelActive():TCP链路建立成功,Channel激活事件;
3)ChannelHandlerContext.fireChannelRead(Object):读事件;
4)ChannelHandlerContext.fireChannelReadComplete():读操作完成通知事件;
5)ChannelHandlerContext.fireExceptionCautght(Throwable):异常通知事件;
6)ChannelHandlerContext.fireUserEventTriggered(Object):用户自定义事件;
7)ChannelHandlerContext.fireChannelWritabilityChanged():Channel的可写状态变化通知事件;
8)ChannelHandlerContext.fireChannelInactive():TCP连接关闭,链路不可用通知事件。

  Outbound事件通常是用户主动发起的网络I/O操作,例如用户发起的连接操作、绑定操作、消息发送等操作。
  触发outbound事件的方法如下:

1)ChannelHandlerContext.bind(SocketAddress, ChannelPromise):绑定本地地址事件;
2)ChannelHandlerContext.connect(SocketAddress, SocketAddress, ChannelPromise):连接服务端事件;
3)ChannelHandlerContext.write(Object, ChannelPromise):发送事件;
4)ChannelHandlerContext.flush():刷新事件;
5)ChannelHandlerContext.read():读事件;
6)ChannelHandlerContext.disconnect(ChannelPromise):断开连接事件;
7)ChannelHandlerContext.close(ChannelPromise):关闭当前Channel事件;


补充:
  ChannelPipeline是一个大的接口,是一个Facade门面模式的实例。它主要包括三部分的接口,

  1. 链表的接口,包括各种遍历,修改链表的操作;
  2. inbound事件的接口,以fireXXXX开头的方法;
  3. outbound事件的接口, 不带fire的方法,比如read, write,bind, connect等。
    inbound,outbound事件是Netty抽象的事件概念,从底层IO事件到用户事件的方向是inbound事件,从用户事件到底层IO事件的方向是outbound事件

  DefaultChannelPipeline是ChannelPipeline接口的具体实现,它处理实际的事件分发。它采用了两个单链表head, tail 来处理inbound,outbound事件。
单链表的节点是ChannelHandlerContext,它通过next, prev两个指针指向前后的节点。head链表的第一个节点是HeadHandler, tail节点的第一个节点是TailHandler。HeadHandler里面封装了Unsafe接口, 来进行实际的IO读写。inbound事件从底层IO开始,outbound事件到底层IO结束,所以inbound事件链的起点从HeadHandler开始,outbound事件链的终点在HeadHandler结束。

连接在pipeline的整个生命周期是:
Server端:
1、ServerSocketChannel: fireChannelRegistered(注册到EventLoop) -> bind(绑定端口)-> fireChannelActive(激活) -> 【 read(注册OP_ACCEPT到SelectorKey) -> fireChannelRead(接收到客户端连接,此时会将客户端连接注册到workerGroup) -> fireChannelReadComplete(读取客户端连接完成) -> 接收下一个连接】 -> 直到最终关闭触发fireChannelUnregistered(从EventLoop中取消注册);
2、SocketChannel: ServerSocketChannel接收到客户端请求后,将其注册到workerGroup -> fireChannelRegistered(注册) -> fireChannelActive (激活) ->【 read(注册OP_READ到SelectorKey) -> fireChannelRead(读取到数据) -> (业务数据处理) -> write(写数据到buffer) -> flush(数据最终发送)/ writeAndFlush(前两个操作的组合) -> fireChannelReadComplete(读取过程结束)】 -> fireChannelInactive(连接关闭) -> fireChannelUnregistered(从EventLoop中取消注册);

Client端:
SocketChannel: fireChannelRegistered(注册到EventLoop) -> connect(连接server) -> fireChannelActive(连接成功) -> 【 read(注册OP_READ) -> write(写数据到buffer) -> flush(数据最终发送) / writeAndFlush(前两个操作的组合) -> fireChannelRead(读取到server传回的数据) -> (业务数据处理) -> fireChannelReadComplete(读取完成)】-> fireChannelInactive(接收关闭连接的请求)-> close(关闭连接) -> fireChannelUnregistered(从EventLoop中取消注册);
上面是一个简化的监听、连接、处理、关闭流程,实际的流程会更加复杂。

Netty 是一个基于 NIO 的客户端、服务器端编程框架,使用 Java 语言编写。它提供了一种高效、可靠、可扩展的异步事件驱动网络编程模型,可以简化网络编程的开发流程。 下面是 Netty 的源码剖析: 1. Bootstrap 类:这是 Netty 启动类,它提供了启动客户端和服务器的方法。其中,ServerBootstrap 类用于启动服务器端应用,Bootstrap 类用于启动客户端应用。 2. Channel 类:这是 Netty 中最核心的类,表示一个通道,可以用来进行数据的读写操作。它继承了 Java NIO 中的 Channel 接口,并添加了一些新的方法和属性,如ChannelPipelineChannelHandlerContext 等。 3. ChannelPipeline 类:这是 Netty 中的另一个核心类,表示一组 ChannelHandler 的有序列表,用于管理数据的处理流程。在 Netty 中,一个 Channel 对象可以有多个 ChannelPipeline 对象,每个 ChannelPipeline 对象包含多个 ChannelHandler 对象。 4. ChannelHandlerContext 类:这是 Netty 中的上下文对象,表示一个 ChannelHandler 对象和它所在的 ChannelPipeline 对象之间的关联关系。它提供了一些方法,可以访问 ChannelPipeline 中的其他 ChannelHandler 对象。 5. ChannelFuture 类:这是 Netty 中的异步操作结果对象,表示一个异步操作的状态和结果。当一个异步操作完成时,会通知关联的 ChannelFuture 对象,从而使应用程序能够得到异步操作的结果。 6. EventLoop 类:这是 Netty 中的事件循环对象,用于处理所有的 I/O 事件和任务。在 Netty 中,一个 EventLoop 对象会被多个 Channel 对象共享,它负责调度和执行所有与这些 Channel 相关的事件和任务。 7. ByteBuf 类:这是 Netty 中的字节缓冲区对象,用于存储和操作字节数据。与 Java NIO 中的 ByteBuffer 对象相比,ByteBuf 提供了更加灵活和高效的读写方式。 8. ChannelHandler 接口:这是 Netty 中的处理器接口,用于处理读写事件和状态变化事件。它提供了多个方法,如 channelActive、channelRead、channelWrite 等,用于处理不同类型的事件。 以上是 Netty 的源码剖析,了解这些核心类和接口可以更好地理解和使用 Netty 框架。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值