netty源码解解析(4.0)-9 ChannelPipleline的默认实现-链表管理

 o.netty.channel.DefaultChannelPipeline implements ChannelPipleline

 

DefaultChannelPiple给出了ChannelPipleline的默认实现。ChannelPipleline是一个双向链表,本章的内容是分析默认实现中双向链表的实现。

 

双向列表的的数据结构

  DefaultChannelPiple使用了三种节点类型: HeadContext, TailContext, DefaultChannelHandlerContext,这三中类型都是派生自AbstractChannelHandlerContext,这个抽象类中有双向链表所需要的两个关键属性next和prev。链表的初始化代码在构造方法中。

复制代码
1 protected DefaultChannelPipeline(Channel channel) { 2         this.channel = ObjectUtil.checkNotNull(channel, "channel"); 3 4         tail = new TailContext(this); 5         head = new HeadContext(this); 6  7         head.next = tail; 8         tail.prev = head; 9  }
复制代码

 

  构造方法的第4-8行,时候是链表的初始化代码。HeadContext是链表头的类型,TailContext是链表尾的类型,这两个类型是DefaultChannelPiple的内部类。链表的头和尾节点是不持有channelHandler的,相比于中间节点,这两个节点比较特殊。有专门的方法用来创建中间节点,如下所示:

复制代码
1 private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) { 2         return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler); 3 }
复制代码

 

添加channelHandler

  向链表中添加channelHandler的方法有两大类型:

  1. addFirst,addLast
  2. addAfter,addBefore

  在ChannelPiple中,每一个handler是有名字的,如果用户没有给handler命名,在添加过程中会为它生成一个不重复的名字。如果用户给handler命名重复,添加handler将会失败同时抛出异常。两种类型的添加方法最大的不同之处在于,第1中会把新节点添加在head之后或tail之前。第2种必须先要首先找到指定名字的节点,然后把新节点添加到这个节点之后或之前。如果没找到指定名字的节点也会导致添加失败同时抛出异常。下面以addAfter为例分析添加过程。

复制代码
 1 public final ChannelPipeline addAfter(  2             EventExecutorGroup group, String baseName, String name, ChannelHandler handler) {  3         final AbstractChannelHandlerContext newCtx;  4         final AbstractChannelHandlerContext ctx;  5  6         synchronized (this) {  7             checkMultiplicity(handler);  8             name = filterName(name, handler);  9             ctx = getContextOrDie(baseName); 10  11             newCtx = newContext(group, name, handler); 12  13             addAfter0(ctx, newCtx); 14 15             EventExecutor executor = newCtx.executor(); 16             if (!executor.inEventLoop()) { 17                 newCtx.setAddPending(); 18                 executor.execute(new Runnable() { 19                     @Override 20                     public void run() { 21                         callHandlerAdded0(newCtx);22                     } 23                 }); 24                 return this; 25             } 26         } 27         callHandlerAdded0(newCtx); 28         return this; 29 }
复制代码

 

  第8行,filterName方法,确保handler有一个名字,如果name==null, 生成一个不重复的名字。然后检查是否有重名的,如果用户指定名字重复抛出异常。

  第9行,找到baseName对应的节点,如果没有抛出异常。

  第11行, 创建新的节点,这个节点将持有hanler,同时给这个节点分配一个eventExecutor。

  第13行,添加链表节点的操作。

  第21,27行,调用handler的handlerAdded方法,如果捕捉到异常,从链表中删除这个刚刚添加的节点,然后调用handler的handlerRemoved方法, 调用fireExceptionCaught方法触发异常事件。

  其它几个添加方法几个add方法和addAfter大致相同。addBefore是把addAfter0变成了addBefore0。addFirst中没有getContextOrDie调用,把addAfter0替换陈addFirst0。addLast在addFirst的基础上把addFirst0替换成addLast0。

  名字是维护链表节点的一个重要因素,DefaultChannelPipleline需要确保链表中的每个节点的名字都重复,这样它才能通过名字找到一个唯一的节点。用户添加一个handler时,如果由于用户命名不当导致的名字重复,这个handler将会被拒绝添加的链表中。如果用户以匿名方式添加handler,添加之前DefaultChannelPipleline会为这个handler生成一个不重复的名字,这个功能在filterName方法中实现。

复制代码
 private String filterName(String name, ChannelHandler handler) {         if (name == null) {             return generateName(handler);         }         checkDuplicateName(name);         

                    
                
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信