Netty的ChannelPipeline

1、如何理解Netty的ChannelPipeline
     在Netty中,每个Channel被创建的时候都需要被关联一个对应的pipeline(通道),这种关联关系是永久的(整个程序运行的生命周期中)。ChannelPipeline可以理解成一个消息( 或消息事件,ChanelEvent)流转的通道,在这个通道中可以被附上许多用来处理消息的handler,当消息在这个通道中流转的时候,如果有与这个消息类型相对应的handler,就会触发这个handler去执行相应的动作。它实现了Intercepting Filter模式(个人理解与Filter Chain模式类似)。

可以通过下面这句话来理解一些相关的概念:ChannelEvents are processed by ChannelHandlers in a ChannelPipeline

2、ChannelPipeline的创建方式
     Netty的作者建议采用辅助类 org.jboss.netty.channel.Channels的静态无参函数pipeline来创建一个ChannelPipeline对象,从源代码上看其实就是调用了DefaultChannelPipeline的无参构造函数,返回new出来的对象而已。

3、消息的流转
     Netty中消息的流转有两个方向:上行、下行
     因此,处理消息事件(ChannelEvent)的处理器(ChannelHandler)大致可以分为两个方向:ChannelUpstreamHandler、ChannelDownstreamHandler
   
   *                                       I/O Request
 *                                     via  {@link Channel}  or
 *                                  {@link ChannelHandlerContext}
 *                                           |
 *  + ---------------------------------------- + ---------------  +
 *  |                  ChannelPipeline       |               |
 *  |                                       \|/              |
 *  |  +  ---------------------- +  + -----------  + ------------ +  |
 *  |  | Upstream Handler  N  |  | Downstream Handler  1  |  |
 *  |  +  ---------- + -----------  +  + ----------- +  ------------ +  |
 *  |            /|\                         |               |
 *  |             |                         \|/              |
 *  |  +  ---------- + -----------  +  + ----------- +  ------------ +  |
 *  |  | Upstream Handler N  - 1 |  | Downstream Handler  2  |  |
 *  |  +  ---------- + -----------  +  + ----------- +  ------------ +  |
 *  |            /|\                         .               |
 *  |             .                          .               |
 *  |     [ sendUpstream() ]        [ sendDownstream() ]     |
 *  |     [ + INBOUND data ]        [ + OUTBOUND data  ]     |
 *  |             .                          .               |
 *  |             .                         \|/              |
 *  |  +  ---------- + -----------  +  + ----------- +  ------------ +  |
 *  |  | Upstream Handler  2  |  | Downstream Handler M  - 1 |  |
 *  |  +  ---------- + -----------  +  + ----------- +  ------------ +  |
 *  |            /|\                         |               |
 *  |             |                         \|/              |
 *  |  +  ---------- + -----------  +  + ----------- +  ------------ +  |
 *  |  | Upstream Handler  1  |  | Downstream Handler  M  |  |
 *  |  +  ---------- + -----------  +  + ----------- +  ------------ +  |
 *  |            /|\                         |               |
 *  + ------------- +  -------------------------- + ---------------  +
 *                |                         \|/
 *  + ------------- +  -------------------------- + ---------------  +
 *  |             |                          |               |
 *  |     [ Socket.read() ]          [ Socket.write() ]      |
 *  |                                                        |
 *  |  Netty Internal I/O Threads (Transport Implementation) |
 *  + -------------------------------------------------------- +

以upstream为例,从socket读出来的数据,逐渐往上进行传递,即原始数据依次往上被进行解析,可能因为所需要的业务数据外面包裹着许多其它附加的数据。如加密、验证、签名等。
downstream与此相反,即把业务数据进行一层层地加密、签名等处理后,写到socket中发出去。这个模型其实可以参考(TCP的七层模型)

4 、pipeline中的handlers顺序如何理解
在添加handler的时候,如果理解顺序。可以把pipeline理解成一个队列,addLast是添加到队列的尾部,addFirst是添加到队列的头部。
upstream的方向是从头到尾,downstream的方向是从尾到头。

5、 多线程环境
ChannelPipeline中的handler是可以在任何时候动态地增加或者删除的,因为ChannelPipeline本身是线程安全的(这点由Netty本身去保证)

6、 ChannelPipeline实例具体解析
在对ChannelPipeline进行解析时,以Netty提供的一个默认实现DefaultChannelPipeline来进行解析,先来看下这个类的一些成员变量(去除日志相关成员logger)
      static  final  ChannelSink discardingSink =  new  DiscardingChannelSink();
     private  volatile  Channel channel;
     private  volatile  ChannelSink sink;
     private  volatile  DefaultChannelHandlerContext head;
     private  volatile  DefaultChannelHandlerContext tail;
     private  final  Map name2ctx =
         new  HashMap(4);
discardingSink是用来处理上行到最上,或者下行到最下时的对消息事件的处理,这个可以不用太关注

主要来关注下head、tail、name2ctx,head和tail这一看就是链表的头和尾,可以进一步猜想pipeline中的handlers是按照一个链表排列的。name2ctx用一个map结构提供名称到handler的映射关系(通过handlerContext可以找到相应的handler)。

下面以addLast为例来进行解析
public  synchronized  void  addLast(String name, ChannelHandler handler) {
         if  (name2ctx.isEmpty()) {
            init(name, handler);
        }  else  {
            checkDuplicateName(name);
            DefaultChannelHandlerContext oldTail = tail;
            DefaultChannelHandlerContext newTail =  new  DefaultChannelHandlerContext(oldTail,  null , name, handler);

            callBeforeAdd(newTail);

            oldTail.next = newTail;
            tail = newTail;
            name2ctx.put(name, newTail);

            callAfterAdd(newTail);
        }
    }
依赖DefaultChannelHandlerContext来实现队列结构,在 DefaultChannelHandlerContext中有指向前一个和后一个的引用,就不细看,只用看一个 DefaultChannelHandlerContext的成员变量即可。
private  final  class  DefaultChannelHandlerContext  implements  ChannelHandlerContext {
         volatile DefaultChannelHandlerContext next;
        volatile DefaultChannelHandlerContext prev;
         private  final  String name;
         private final ChannelHandler handler;
         private  final  boolean  canHandleUpstream;
         private  final  boolean  canHandleDownstream;
         private  volatile  Object attachment;

你可能感兴趣的:(Java)