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

io.netty.channel.DefaultChannelPipeline implements ChannelPipleline
 

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

 

双向列表的的数据结构ide

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

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的,相比于中间节点,这两个节点比较特殊。有专门的方法用来建立中间节点,以下所示:this

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

 

添加channelHandlerspa

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

  1. addFirst,addLast
  2. addAfter,addBefore

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

 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, 生成一个不重复的名字。而后检查是否有重名的,若是用户指定名字重复抛出异常。code

  第9行,找到baseName对应的节点,若是没有抛出异常。blog

  第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);
        return name;
}

 

  generateName方法负责为匿名的handler生成一个名字,checkDuplicateName负责验证用户提供的名字是否重复。名字的生成规则是handler的类型名+"#n",假设你的handler的类型名是com.test.YourClass, 那么生成名字将是YourClass#0, YourClass#1, ..., YourClass#n。

 

删除链表节点

  全部的remove方法最终都会调用到private AbstractChannelHandlerContext remove(final AbstractChannelHandlerContext ctx)方法.

 1 private AbstractChannelHandlerContext remove(final AbstractChannelHandlerContext ctx) {
 2         assert ctx != head && ctx != tail;
 3 
 4         synchronized (this) {
 5             remove0(ctx);
 6 
 7             EventExecutor executor = ctx.executor();
 8             if (!executor.inEventLoop()) {
 9                 executor.execute(new Runnable() {
10                     @Override
11                     public void run() {
12                         callHandlerRemoved0(ctx);
13                     }
14                 });
15                 return ctx;
16             }
17         }
18         callHandlerRemoved0(ctx);
19         return ctx;
20 }

 

  5行,从链表结构中删除handler。

  12,18行, 调用handler的handlerRemoved方法。

 

链表节点查找

  查找方法get最终都会调用内部的context0方法,这个方法是纯粹的链表操做,比较单纯。

 

替换链表节点

  全部的replace方法最终都会调用内部的replace方法:

  private ChannelHandler replace(final AbstractChannelHandlerContext ctx, final String newName, ChannelHandler newHandler)

  这个方法代码结构与addAfter类似,不一样的是在链表操做上是一个替换操做,以后会先调用被替换handler的handlerRemoved方法,而后调用新handler的handlerAdded方法。

 

链表操做会handler方法之间的调用关系

链表方法 ChannelHandler方法
addBefore,addAfter,addFirst,addLast handlerAdded  
get
remove,removeFirst,removeLast handleRemoved
replace handleRemoved, handlerAdded
相关文章
相关标签/搜索