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
相關文章
相關標籤/搜索