Netty源码深度解析-Pipeline(1) Pipeline的构造

导读

原创文章,转载请注明出处。

本文源码地址:netty-source-code-analysis

本文所使用的netty版本4.1.6.Final:带注释的netty源码

Pipeline这个词翻译过来就是“流水线”的意思,读到这里有了解过设计模式的同学应该已经想到了,这里用到的是“责任链模式”。本文我们将以DefaultChannelPipeline为例看一下Pipeline的构造以及其中重要的数据结构。

1 和Pipeline相关的其他组件

1.1 ChannnelHandler

这是ChannelHandler中的注释,翻译过来就是“处理IO事件或者拦截IO操作,并且将其向ChannelPipeline中的下一个handler传递”,说白了就是在责任链中注册的一系列回调方法。

Handles an I/O event or intercepts an I/O operation, and forwards it to its next handler in
its ChannelPipeline

这里的I/O event就是很多书中提到的“入站事件”,而I/O operation就是很多书中提到的“出站事件”,前面我说过,这里我并不准备这么叫,按我的理解我习惯把这两者称之为“事件”和“命令”。很显然这里eventoperation的含义是不一样的,event更多地多表示事件发生了,我们被动地收到,而operation则表示我们主动地发起一个动作或者命令。

1.2 ChannelHandlerContext

每一个ChannelHandler在被添加进ChannelPipeline时会被包装进一个ChannelHandlerContext。有两个特殊的ChannelHandlerContext除外,分别是HeadContextTailContextHeadContext继承了ChannelInBoundHandlerChannelOutBoundHandler,而TailContext继承了ChannelInBoundHandler
每个ChannelHandlerContext中有两个指针nextprev,这是用来将多个ChannelHandlerContext构成双向链表的。

2 Pipeline的构造方法

我们以DefaultChannelPipeline为例,从它的构造方法开始。这里首先将Channel保存到Pipeline的属性中,又初始化了两个属性succeedFuturevoidPromise。这是两个特殊的可以共享的Promise,这两个Promise不是重点,不理解也没关系。

接下来的tailhead是两个特殊的ChannelHandlerContext,这两个是Pipeline中的重要组件。

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

Pipeline在执行完构造方法以后的结构如下图所示,headtail构成了最简单的双向链表。
Pipeline初始化
图中蓝色填充的就是ChannelHandlerContext,目前只有HeadContextTailContextChannelHandlerContext中的较窄的矩形表示ChannelHandler,由于HeadContextTailContext并没有包含ChannelHandler,而是继承ChannelHandler,所以这里我们用虚线表示。上下贯通的ChannelHandler表示既是ChannelInBoundHandler又是ChannelOutBoundHandler,只有上半部分的表示是ChannelInBoundHandler,只有下半部分的表示是ChannelOutBoundHandler

3 添加ChannelHandler

ChannelPipeline中有很多以add开头的方法,这些方法就是向ChannelPipeline中添加ChannelHandler的方法。

  • addAfter:向某个ChannelHandler后边添加
  • addBefore:向某个ChannelHandler前面添加
  • addFirst:添加到头部,不能在head的前面,而是紧挨着head,在head的后面
  • addLast:添加到尾部,不能在tail的后面,而是紧挨着tail,在tail的前面

我们以最常用的的addLast方法为例来分析一下Pipeline中添加ChannelHandler的操作。
这里所贴出的addLast方法其实我们已经在“服务端启动流程”这篇文章中打过照面了。方法参数中的EventExecutorGroup意味着我们可以为这个ChannelHandler单独设置Excutor而不使用Channel所绑定的EventLoop,一般情况下我们不这么做,所以group参数为null

这里先把ChannelHandler包装成ChannelHandlerContext,再添加到尾部,随后调用ChannelHandlerHandlerAdded方法。

在调用HandlerAdded方法时有一点问题,添加ChannelHandler的操作不需要在EventLoop线程中进行,而HandlerAdded方法则必须在EventLoop线程中进行。也就是说存在添加Handler时还未绑定EventLoop的情况,此时则调用newCtx.setAddPending()将当前HandlerContext设置为ADD_PENDING状态,并且调用callHandlerCallbackLater(newCtx, true)将一个异步任务添加到一个单向队链表中,即pendingHandlerCallbackHead这个链表。

如果当前已经绑定了EventLoop,则看当前调用线程是否为EventLoop线程,如果不是则向EventLoop提交一个异步任务调用callHandlerAdded0方法,否则直接调用callHandlerAdded0方法。

下面咱们依次分析一下newContextcallHandlerCallbackLatercallHandlerAdd0方法。

@Override
    public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
   
        final AbstractChannelHandlerContext newCtx;
        synchronized (this) {
   
           //先把`handler`包装成`HandlerContext`
            newCtx = newContext(group, filterName(name, handler), handler);
            //添加到尾部
            addLast0(newCtx);
            //如果还未绑定`EventLoop`则稍后再发起对`HandlerAdded`方法的调用。
            if (!registered) {
   
                newCtx.setAddPending();
                callHandlerCallbackLater(newCtx, true);
                return this
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值