Netty源码------Pipeline详细分析

本文详细分析了Netty中的Channel与ChannelPipeline的关系,解释了Pipeline的初始化过程,包括ChannelInitializer的添加、自定义ChannelHandler的插入、ChannelHandler的命名规则以及Pipeline的事件传播机制,特别是Outbound和Inbound事件的传播方式。此外,还介绍了Channel的生命周期与Handler的各种使用场景。
摘要由CSDN通过智能技术生成

Netty源码------Pipeline详细分析

目录

Netty源码------Pipeline详细分析

1、Channel 与ChannelPipeline

2、再探ChannelPipeline 的初始化:

3、ChannelInitializer 的添加

4、自定义ChannelHandler 的添加过程

5、ChannelHandler 默认命名规则

6、Pipeline 的事件传播机制

6.1 Outbound 事件传播方式

6.2 Inbound 事件传播方式

6.3 Pipeline 事件传播小结

7、Handler 的各种姿势

8、Channel 的生命周期


1、Channel 与ChannelPipeline

  相信大家都已经知道,在Netty 中每个Channel 都有且仅有一个ChannelPipeline 与之对应,它们的组成关系如下:

  通过上图我们可以看到, 一个Channel 包含了一个ChannelPipeline , 而ChannelPipeline 中又维护了一个由ChannelHandlerContext 组成的双向链表。这个链表的头是HeadContext,链表的尾是TailContext,并且每个ChannelHandlerContext 中又关联着一个ChannelHandler。图示给了我们一个对ChannelPipeline 的直观认识,但是实际上Netty 实现的Channel 是否真的是这样的呢?我们继续用源码说话。在前我们已经知道了一个Channel 的初始化的基本过程,下面我们再回顾一下。下面的代码是AbstractChannel 构造器:

protected AbstractChannel(Channel parent) {
        this.parent = parent;
        id = newId();
        unsafe = newUnsafe();
        pipeline = newChannelPipeline();
    }

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

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 互相指向, 构成一个双向链表。

  特别注意的是:我们在开始的示意图中head 和tail 并没有包含ChannelHandler,这是因为HeadContext 和TailContext继承于AbstractChannelHandlerContext 的同时也实现了ChannelHandler 接口了,因此它们有Context 和Handler的双重属性。

2、再探ChannelPipeline 的初始化:

  前面的学习我们已经对ChannelPipeline 的初始化有了一个大致的了解,不过当时重点没有关注ChannelPipeline,因此没有深入地分析它的初始化过程。那么下面我们就来看一下具体的ChannelPipeline 的初始化都做了哪些工作吧。先回顾一下,在实例化一个Channel 时,会伴随着一个ChannelPipeline 的实例化,并且此Channel 会与这个ChannelPipeline相互关联,这一点可以通过NioSocketChannel 的父类AbstractChannel 的构造器予以佐证:

protected AbstractChannel(Channel parent) {
        this.parent = parent;
        id = newId();
        unsafe = newUnsafe();
        pipeline = newChannelPipeline();
    }

  当实例化一个NioSocketChannel 是,其pipeline 字段就是我们新创建的DefaultChannelPipeline 对象。可以看到,在DefaultChannelPipeline 的构造方法中,将传入的channel 赋值给字段this.channel,接着又实例化了两个特殊的字段:tail 与head,这两个字段是一个双向链表的头和尾。其实在DefaultChannelPipeline 中,维护了一个以AbstractChannelHandlerContext 为节点的双向链表,这个链表是Netty 实现Pipeline 机制的关键。再回顾一下head和tail 的类层次结构:

  从类层次结构图中可以很清楚地看到,head 实现了ChannelInboundHandler与ChannelOutboundHandler,而tail 实现了ChannelOutboundHandler 接口,并且它们都实现了ChannelHandlerContext 接口, 因此可以说head 和tail 即是一个ChannelHandler,又是一个ChannelHandlerContext。接着看HeadContext与TailContext 构造器中的代码:

HeadContext(DefaultChannelPipeline pipeline) {
            super(pipeline, (EventExecutor)null, DefaultChannelPipeline.HEAD_NAME, false, true);
            this.unsafe = pipeline.channel().unsafe();
            this.setAddComplete();
}
TailContext(DefaultChannelPipeline pipeline) {
            super(pipeline, (EventExecutor)null, DefaultChannelPipeline.TAIL_NAME, true, false);
            this.setAddComplete();
}

  我们可以看到,链表中head 是一个ChannelOutboundHandler,而tail 则是一个ChannelInboundHandler。它调用了父类AbstractChannelHandlerContext 的构造器,并传入参数inbound = false,outbound = true。而TailContext 的构造器与HeadContext 的相反,它调用了父类AbstractChannelHandlerContext 的构造器,并传入参数inbound = true,outbound = false。即header 是一个OutBoundHandler,而tail 是一个InBoundHandler。

3、ChannelInitializer 的添加

  前面我们已经分析过Channel 的组成,其中我们了解到,最开始的时候ChannelPipeline 中含有两个ChannelHandlerContext(同时也是ChannelHandler),但是这个Pipeline 并不能实现什么特殊的功能,因为我们还没有给它添加自定义的ChannelHandler。通常来说,我们在初始化Bootstrap,会添加我们自定义的ChannelHandler,就以我们具体的客户端启动代码片段来举例:

 

Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.handler(new ChannelInitializer<SocketChannel>() {
  @Override
  protected void initChannel(SocketChannel ch) throws Exception {
    ChannelPipeline pipeline = ch.pipeline();
    pipeline.addLast(new ChatClientHandler(nickName));
  }
});

 

  上面代码的初始化过程,相信大家都不陌生。在调用handler 时,传入了ChannelInitializer 对象,它提供了一个initChannel()方法给我我们初始化ChannelHandler。最后将这个匿名的Handler保存到AbstractBootstrap中。那么这个初始化过程是怎样的呢?下面我们来揭开它的神秘面纱。

  ChannelInitializer 实现了ChannelHandler,那么它是在什么时候添加到ChannelPipeline 中的呢?通过代码跟踪,我们发现它是在Bootstrap 的init()方法中添加到ChannelPipeline 中的,其代码如下(以客户端为例):

 

void init(Channel channel) throws Exception {
        ChannelPipeline p = channel.pipeline();
        p.addLast(new ChannelHandler[]{this.config.handler()});
     。。。。。。
}
//AbstractBootstrapConfig
public final ChannelHandler handler() {
  return this.bootstrap.handler();
}
//AbstractBootstrap
final ChannelHandler handler() {
        return this.handler;
}

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

  这时候,有小伙伴可能就有疑惑了,我明明插入的是一个ChannelInitializer 实例,为什么在ChannelPipeline 中的双向链表中的元素却是一个ChannelHandlerContext 呢?我们继续去源码中寻找答案。

  刚才,我们提到,在Bootstrap 的init()方法中会调用p.addLast()方法,将ChannelInitializer 插入到链表的末端:

 

public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
        final AbstractChannelHandlerContext newCtx;
        synchronized(this) {
            checkMultiplicity(handler);
            newCtx = this.newContext(group, this.filterName(name, handler), handler);
       this.addLast0(newCtx);
 }
private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) {
    return new DefaultChannelHandlerContext(this, this.childExecutor(group), name, handler);
}

 

  addLast()有很多重载的方法,我们只需关注这个比较重要的方法就行。上面的addLast()方法中,首先检查ChannelHandler 的名字是否是重复,如果不重复,则调用newContex()方法为这个Handler 创建一个对应的DefaultChannelHandlerContext 实例,并与之关联起来(Context 中有一个handler 属性保存着对应的

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值