netty 之Channel、pipeline,ChannelHandler和ChannelHandlerContext之间的关系

 Channel和ChannelPipeline,ChannelHandler、ChannelHandlerContext之间的关系 ( 以下分别简写为chann或channel, pipeline,handler,context或ctx )  

简谈启动过程的初始化

服务端、客户端的启动都是类似的, 主要有几个方法: bs.group(bossGroup) .channel(NioSocketChannel.class) .option(ChannelOption.SO_KEEPALIVE, true) .handler(new ChannelInitializer() { ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast(new StringDecoder(Charset.forName("GBK"))); });   其中做了很多初始配置的工作,这里略去,不过需要注意的的是ChannelInitializer,ChannelInitializer 直接继承于ChannelInboundHandlerAdapter,也是一个ChannelHandler ,就是处理器,不过它的作用一般就是初始化一个pipeline,如上,通过pipeline.addLast 可以添加很多的处理器。这些处理器Handler就是 用户逻辑,是关键的业务代码。   每条pipeline 上可以有多个handler,这个逻辑就是每次连接成功之后,会执行ChannelInitializer, 即上面的 .handler方法设置的 Handler,是 初始化的作用  

Channel

Channel 是接口,其两个主要的顶层的实现是 AbstractChannel、其成员变量有Channel parent、Unsafe、SocketAddress localAddress、remoteAddress、EventLoop。   其中 Unsafe 的主要实现是AbstractUnsafe、 AbstractNioUnsafe,后者是对java 的nio做了封装。   所以其实简单来说,netty 的Channel 就是java 的nio的Channel ,也就是 对端的实际发出的连接   从channel 的主要实现AbstractChannel看,它有一个DefaultChannelPipeline指针 pipeline netty 之Channel、pipeline,ChannelHandler和ChannelHandlerContext之间的关系

 

 

  一个channel 就是一个连接,就是每次和对端建立连接之后,都会创建一个 channel, 不多不少   然后channel 对应一条 pipeline,每条pipeline 上可以有多个handler,从构造器可以看出来:   protected AbstractChannel(Channel parent) { this.parent = parent; id = newId(); unsafe = newUnsafe(); pipeline = newChannelPipeline(); }   不过是不是1:1,这里还看不出来。因为其实有可能是 ctx1 -> handler1, 然后 handler1 -> ctx2, ctx2 -> handler2 这样的链式关系。链式关系显然不是简单直观的 1:1 关系。   但是跟进去会发现,确实是1:1 关系:
protected DefaultChannelPipeline newChannelPipeline() {
        return new DefaultChannelPipeline(this); // 正是这里的this,完成了双向绑定
}

// // 这里的参数是Channel , 相当于是这里做了双向绑定
protected DefaultChannelPipeline(Channel channel) { // 这里的channel就是前面的AbstractChannel实例
        this.channel = ObjectUtil.checkNotNull(channel, "channel");
        succeededFuture = new SucceededChannelFuture(channel, null);
        voidPromise =  new VoidChannelPromise(channel, true);


        tail = new TailContext(this);// 为pipeline 和TailContext绑定, 这里也是双向绑定
        head = new HeadContext(this);// 这里也是双向绑定

        head.next = tail; // 建立好pipeline的管道链条
        tail.prev = head;
 }

 

ChannelPipeline

ChannelPipeline 的直接实现就一个:DefaultChannelPipeline, 从源码看DefaultChannelPipeline, 它主要有一个Channel、两个AbstractChannelHandlerContext,分别是 head、tail,分别是pipeline的头和尾,两者必不可少,而且是固定不可变的;   另外 还有表示 executor group 和 executor 的对应关系: Map<eventexecutorgroup, eventexecutor=""> childExecutors; netty 之Channel、pipeline,ChannelHandler和ChannelHandlerContext之间的关系 其 里面的方法值得好好看看, 比如: @Override public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {   return tail.bind(localAddress, promise); } 里面写了很多很多,每一个方法都值得推敲,都是准确无误  

ChannelHandler

ChannelHandler 几乎没有成员变量,比如ChannelHandlerAdapter 只有一个 boolean added: netty 之Channel、pipeline,ChannelHandler和ChannelHandlerContext之间的关系 ChannelInboundHandler 提供了入站事件的处理入口。类似地,ChannelInboundHandlerAdapter 提供了默认空实现,以方便其他的或 自定义的 Handler继承,然后只需实现自己想实现的、感兴趣的方法即可。   ChannelHandler 的子类包括ChannelOutboundHandlerAdapter、ChannelInboundHandlerAdapter、ChannelDuplexHandler、MessageToMessageEncoder、MessageToByteEncoder 基本上是没什么成员变量的。 可见它是无状态的,他和其他组件有关系, 但基本是他被其他组件使用。   ChannelHandler 虽然成员变量不多,但是方法众多,要处理好这些,其实并不容易。netty为我们提供了非常非常繁多的各种处理器, 也是非常复杂的,此处按下不表,后文再叙。我们基本上只要拿来用即可。但是前提是知道如何使用。   ChannelHandler 看起来很单薄? 确实是这样,它没有netty框架相关的成员变量,它甚至可以没有成员变量。因为它是 用户需要去实现的处理器, netty已经做了很好的封装,它已经是netty 核心内容之外的东西了。 所以它就不需要 再直接对接 netty的 reactor 打交道。   它只需要处理reactor 模型派发的 各种事件即可。   但是呢,它和它的两个直接子接口,提供了 handlerAdded 等触发。 ChannelHandlerAdapter 提供了默认空实现。   所有的这些接口方法, 都至少存在一个ChannelHandlerContext类型的参数,然后通过ChannelHandlerContext 就可以获取所需的 pipeline、 channel、eventloop 等。   ChannelOutboundHandler 提供了出站事件的处理入口。类似地,ChannelOutboundHandlerAdapter提供了默认空实现。     ChannelHandlerContext 理解ChannelHandlerContext 是非常关键的。 它处在中间,上下都是文,可谓是 上下文啊。 不过, ChannelHandlerContext 到底是什么? 其实就是对ChannelHandler进行了 上下的 封装,使得它可以不用再去和pipeline、 channel、eventloop打交道了。不用去,也不需要去理解哪些难搞的概念,如 pipeline、 channel、eventloop,使得编程稍微“简单”了些。   观察 ChannelHandlerContext 发现,其实他就是已经对 pipeline、 channel、eventloop 做了很多工作。 复杂的自己搞定,然后把简单的事情 给 ChannelHandler。其实ChannelHandlerContext extends AttributeMap, ChannelInboundInvoker, ChannelOutboundInvoker,然后基本上ChannelHandler所有的方法都是直接调用ctx完成的,而ctx又是封装、调用了pipeline、 channel、eventloop,或者就把事件传递给了 next。.   话虽如此,但是,很多时候,ChannelHandler还是需要去获取 pipeline、 channel、eventloop 之类的。 比如,我可能需要从头发送信息。   ChannelHandlerContext的直接实现是AbstractChannelHandlerContext ,它主要就是包含了向后向前的指针,next、prev,然后还有就是 顺序: ordered。 还有一个DefaultChannelPipeline指针 pipeline : netty 之Channel、pipeline,ChannelHandler和ChannelHandlerContext之间的关系

 

 

  AbstractChannelHandlerContext的具体实现 主要有3个:HeadContext、TailContext、DefaultChannelHandlerContext   对于普通的 ChannelHandler 自然是使用 DefaultChannelHandlerContext, netty 之Channel、pipeline,ChannelHandler和ChannelHandlerContext之间的关系

 

 

  代码在DefaultChannelPipeline, private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) { return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler); }   其实newContext还是在pipeline的 addXxx方法中 被调用的。   
    @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);
...
        }
        callHandlerAdded0(newCtx);
        return this;
    }
    private void addLast0(AbstractChannelHandlerContext newCtx) { // 加入到链表之中
        AbstractChannelHandlerContext prev = tail.prev;
        newCtx.prev = prev;
        newCtx.next = tail;
        prev.next = newCtx;
        tail.prev = newCtx;
    }

 

  DefaultChannelHandlerContext 构造器的参数的this是pipeline,而pipeline的头尾之间有很多ctx,所以pipeline和ctx关系 可以认为是 1 : N 。 当然,这个关系不是简单的1个管道 : 一个集合的ctx ,而是1个管道 : 一个链表的ctx。   newContext的第三个参数,即DefaultChannelHandlerContext 构造器第四个参数handler,是创建时候就分配给ctx 的。显然,因为每次创建ctx 分配一个handler,handler有可能不是同一个,所以,handler:ctx 肯定是 1:N. 但是 ,是不是1:1,这里还看不出来。但至少是有单向关系的,   事实证明,这个确实是是1:1关系。因为handler 中方法的ctx参数,全都是事件形式传递的。ctx 是在链表之上的,而事件在ctx 组成的链表上传递,然后ctx 把事件交给了其拥有的 handler 进行处理。所以 是1:1关系。   当然,如果handler 可以共享,那么就是handler:ctx = 1:N, 另当别论。    

观察&分析总结

综上,提供观察&分析可见,基本是如下关系: netty 之Channel、pipeline,ChannelHandler和ChannelHandlerContext之间的关系

 

 

  其中,每一条线, 表示一个类的对应的成员变量或者说一个字段、一个指针,把主要字段用直线表示出来,全部画好之后, 得到如上的图。   ChannelPipeline 是连接创建的时候就会创建好的,而Channel 也是一个连接一个, Channel 比ChannelPipeline 是 1:1 的关系。   其中的Handler 基本是固定的,当然,我们也可以动态增删ChannelHandler,上面只画了3个ctx;   ChannelHandler 和ChannelHandlerContext 是1:1 关系,就是说每次确实是建立连接之后,创建一个新的Channel ,然后绑定到唯一的 ChannelPipeline 实例上去... (准确说是 创建,不是绑定. )   可以说 pipeline、handler 关系是 1 : N, 但他们不是直接的关系,原因见上图的链条   ...   PS: 有几个图很流行,但是 看完其实还是晕乎乎的。因为你不去分析总结,那么你永远都是迷迷糊糊..  netty 之Channel、pipeline,ChannelHandler和ChannelHandlerContext之间的关系

 

 netty 之Channel、pipeline,ChannelHandler和ChannelHandlerContext之间的关系

这个图 其实是没错的,但是不够清晰啊!head、 tail 都没有画出来啊!

 

 

netty 之Channel、pipeline,ChannelHandler和ChannelHandlerContext之间的关系  
上一篇:【Unity Shader】渲染纹理(镜子与玻璃)


下一篇:复杂的光照与更复杂的阴影实现过程——ShaderCP9