Netty源碼分析第四章: pipeline
第二節: Handler的添加
添加handler, 我們以用戶代碼為例進行剖析:
.childHandler(new ChannelInitializer<SocketChannel>() { protected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new DelimiterBasedFrameDecoder(Integer.MAX_VALUE, Delimiters.lineDelimiter()[0])); ch.pipeline().addLast(new StringEncoder()); ch.pipeline().addLast(new SimpleHandler()); } });
用過netty的小伙伴們肯定對這段代碼不會陌生, 通過addLast, 可以添加編解碼器和我們自定義的handler, 某一個事件完成之后可以自動調用我們handler預先定義的方法, 具體添加和調用是怎么個執行邏輯, 在我們之后的內容會全部學習到, 以后再使用這類的功能會得心應手
在這里, 我們主要剖析 ch.pipeline().addLast(new SimpleHandler()) 這部分代碼的addLast()方法
首先通過channel拿到當前的pipline, 這個上一小節進行剖析過相信不會陌生
拿到pipeline之后再為其添加handler, 因為channel初始化默認創建的是DefualtChannelPipeline
我們跟到其addLast()方法中:
public final ChannelPipeline addLast(ChannelHandler... handlers) { return addLast(null, handlers); }
首先看到這里的參數其實是一個可變對象, 也就是可以傳遞多個handler, 這里我們只傳遞了一個
我們繼續跟addLast:
public final ChannelPipeline addLast(EventExecutorGroup executor, ChannelHandler... handlers) { if (handlers == null) { throw new NullPointerException("handlers"); } //傳多個參數的時候通過for循環添加
for (ChannelHandler h: handlers) { if (h == null) { break; } addLast(executor, null, h); } return this; }
這里如果傳入多個handler則會循環添加, 我們通常只添加一個
再繼續跟到addLast()方法中去:
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) { final AbstractChannelHandlerContext newCtx; synchronized (this) { //判斷handler是否被重復添加(1)
checkMultiplicity(handler); //創建一個HandlerContext並添加到列表(2)
newCtx = newContext(group, filterName(name, handler), handler); //添加HandlerContext(3)
addLast0(newCtx); //是否已注冊
if (!registered) { newCtx.setAddPending(); callHandlerCallbackLater(newCtx, true); return this; } EventExecutor executor = newCtx.executor(); if (!executor.inEventLoop()) { newCtx.setAddPending(); //回調用戶事件
executor.execute(new Runnable() { @Override public void run() { callHandlerAdded0(newCtx); } }); return this; } } //回調添加事件(4)
callHandlerAdded0(newCtx); return this; }
這部分代碼比較長, 我們拆解為4個步驟:
1.重復添加驗證
2.創建一個HandlerContext並添加到列表
3. 添加context
4. 回調添加事件
首先我們看第一步, 重復添加驗證
我們跟到checkMultiplicity(handler)中:
private static void checkMultiplicity(ChannelHandler handler) { if (handler instanceof ChannelHandlerAdapter) { ChannelHandlerAdapter h = (ChannelHandlerAdapter) handler; if (!h.isSharable() && h.added) { throw new ChannelPipelineException( h.getClass().getName() +
" is not a @Sharable handler, so can't be added or removed multiple times."); } //滿足條件設置為true, 代表已添加
h.added = true; } }
首先判斷是不是ChannelHandlerAdapter類型, 因為我們自定義的handler通常會直接或者間接的繼承該接口, 所以這里為true
拿到handler之后轉換成ChannelHandlerAdapter類型, 然后進行條件判斷
if (!h.isSharable() && h.added) 代表如果不是共享的handler, 並且是未添加狀態, 則拋出異常:
我們可以跟到isSharable()方法中去:
public boolean isSharable() { Class<?> clazz = getClass(); Map<Class<?>, Boolean> cache = InternalThreadLocalMap.get().handlerSharableCache(); Boolean sharable = cache.get(clazz); if (sharable == null) { //如果這個類注解了Sharable.class, 說明這個類會被多個channel共享
sharable = clazz.isAnnotationPresent(Sharable.class); cache.put(clazz, sharable); } return sharable; }
首先拿到當前handler的class對象
然后再從netty自定義的一個ThreadLocalMap對象中獲取一個盛放handler的class對象的map, 並獲取其value
如果value值為空, 則會判斷是否被Sharable注解, 並將自身handler的class對象和判斷結果存入map對象中, 最后返回判斷結果
這說明了被Sharable注解的handler是一個共享handler
從這個邏輯我們可以判斷, 共享對象是可以重復添加的
我們回到checkMultiplicity(handler)方法中:
如果是共享對象或者沒有被添加, 則將ChannelHandlerAdapter的added設置為true, 代表已添加
剖析完了重復添加驗證, 回到addLast方法中, 我們看第二步, 創建一個HandlerContext並添加到列表:
newCtx = newContext(group, filterName(name, handler), handler);
首先看filterName(name, handler)方法, 這個方法是判斷添加handler的name是否重復
跟到filterName方法中:
private String filterName(String name, ChannelHandler handler) { if (name == null) { //沒有名字創建默認名字
return generateName(handler); } //檢查名字是否重復
checkDuplicateName(name); return name; }
因為我們添加handler時候, 不一定會會給handler命名, 所以這一步name有可能是null, 如果是null, 則創建一個默認的名字, 這里創建名字的方法我們就不往里跟了, 有興趣的同學可以自己跟進去看
然后再檢查名字是否重復
我們跟到checkDuplicateName(name)這個方法中:
private void checkDuplicateName(String name) { //不為空
if (context0(name) != null) { throw new IllegalArgumentException("Duplicate handler name: " + name); } }
這里有個context0(name)方法, 我們跟進去:
private AbstractChannelHandlerContext context0(String name) { //遍歷pipeline
AbstractChannelHandlerContext context = head.next; while (context != tail) { //發現name相同, 說明存在handler
if (context.name().equals(name)) { //返回
return context; } context = context.next; } return null; }
這里做的操作非常簡單, 就是將pipeline中, 從head節點往下遍歷HandlerContext, 一直遍歷到tail, 如果發現名字相同則會認為重復並返回HandlerContext對象
我們回到addLast()方法中並繼續看添加創建相關的邏輯:
newCtx = newContext(group, filterName(name, handler), handler)
filterName(name, handler)這步如果並沒有重復則會返回handler的name
我們繼續跟到newContext(group, filterName(name, handler), handler)方法中:
private AbstractChannelHandlerContext newContext(EventExecutorGroup group, String name, ChannelHandler handler) { return new DefaultChannelHandlerContext(this, childExecutor(group), name, handler); }
這里我們看到創建了一個DefaultChannelHandlerContext對象, 構造方法的參數中, 第一個this代表當前的pipeline對象, group為null, 所以childExecutor(group)也會返回null, name為handler的名字, handler為新添加的handler對象
我們繼續跟到DefaultChannelHandlerContext的構造方法中:
DefaultChannelHandlerContext( DefaultChannelPipeline pipeline, EventExecutor executor, String name, ChannelHandler handler) { super(pipeline, executor, name, isInbound(handler), isOutbound(handler)); if (handler == null) { throw new NullPointerException("handler"); } this.handler = handler; }
我們看到首先調用了父類的構造方法, 之后將handler賦值為自身handler的成員變量, HandlerConext和handler關系在此也展現了出來, 是一種組合關系
我們首先看父類的構造方法, 有這么兩個參數:isInbound(handler), isOutbound(handler), 這兩個參數意思是判斷需要添加的handler是inboundHandler還是outBoundHandler
跟到isInbound(handler)方法中:
private static boolean isInbound(ChannelHandler handler) { return handler instanceof ChannelInboundHandler; }
這里通過是否實現ChannelInboundHandler接口來判斷是否為inboundhandler
同樣我們看isOutbound(handler)方法:
private static boolean isOutbound(ChannelHandler handler) { return handler instanceof ChannelOutboundHandler; }
通過判斷是否實現ChannelOutboundHandler接口判斷是否為outboundhandler
在跟到其父類AbstractChannelHandlerContext的構造方法中:
AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor, String name, boolean inbound, boolean outbound) { this.name = ObjectUtil.checkNotNull(name, "name"); this.pipeline = pipeline; this.executor = executor; this.inbound = inbound; this.outbound = outbound; ordered = executor == null || executor instanceof OrderedEventExecutor; }
一切都不陌生了, 因為我們tail節點和head節點創建的時候同樣走到了這里
這里初始化了name, pipeline, 以及標識添加的handler是inboundhanlder還是outboundhandler
我們回到最初的addLast()方法中:
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) { final AbstractChannelHandlerContext newCtx; synchronized (this) { //判斷handler是否被重復添加(1)
checkMultiplicity(handler); //創建一個HandlerContext並添加到列表(2)
newCtx = newContext(group, filterName(name, handler), handler); //添加HandlerContext(3)
addLast0(newCtx); //是否已注冊
if (!registered) { newCtx.setAddPending(); callHandlerCallbackLater(newCtx, true); return this; } EventExecutor executor = newCtx.executor(); if (!executor.inEventLoop()) { newCtx.setAddPending(); //回調用戶事件
executor.execute(new Runnable() { @Override public void run() { callHandlerAdded0(newCtx); } }); return this; } } //回調添加事件(4)
callHandlerAdded0(newCtx); return this; }
我們跟完了創建HandlerContext的相關邏輯, 我們繼續跟第三步, 添加HandlerContext
我們跟進addLast0(newCtx)中:
private void addLast0(AbstractChannelHandlerContext newCtx) { //拿到tail節點的前置節點
AbstractChannelHandlerContext prev = tail.prev; //當前節點的前置節點賦值為tail節點的前置節點
newCtx.prev = prev; //當前節點的下一個節點賦值為tail節點
newCtx.next = tail; //tail前置節點的下一個節點賦值為當前節點
prev.next = newCtx; //tail節點的前一個節點賦值為當前節點
tail.prev = newCtx; }
這一部分也非常簡單, 做了一個指針的指向操作, 將新添加的handlerConext放在tail節點之前, 之前tail節點的上一個節點之后, 熟悉雙向鏈表的同學對此邏輯應該不會陌生, 如果是第一次添加handler, 那么添加后的結構入下圖所示:
4-2-1
添加完handler之后, 這里會判斷當前channel是否已經注冊, 這部分邏輯我們之后再進行剖析, 我們繼續往下走
之后會判斷當前線程線程是否為eventLoop線程, 如果不是eventLoop線程, 就將添加回調事件封裝成task交給eventLoop線程執行, 否則, 直接執行添加回調事件callHandlerAdded0(newCtx)
跟進callHandlerAdded0(newCtx):
private void callHandlerAdded0(final AbstractChannelHandlerContext ctx) { try { ctx.handler().handlerAdded(ctx); ctx.setAddComplete(); } catch (Throwable t) { //忽略代碼
} }
我們重點關注這句
ctx.handler().handlerAdded(ctx);
其中ctx是我們新創建的HandlerContext, 通過handler()方法拿到綁定的handler, 也就是新添加的handler, 然后執行handlerAdded(ctx)方法, 如果我們沒有重寫這個方法, 則會執行父類的該方法
在ChannelHandlerAdapter類中定義了該方法的實現:
@Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { }
我們看到沒做任何操作, 也就是如果我們沒有重寫該方法時, 如果添加handler之后將不會做任何操作, 這里如果我們需要做一些業務邏輯, 可以通過重寫該方法進行實現
以上就是添加handler的有關的業務邏輯