Netty源碼分析第4章(pipeline)---->第2節: handler的添加

 

Netty源碼分析第四章: pipelinehtml

 

第二節: Handler的添加ide

 

 

添加handler, 咱們以用戶代碼爲例進行剖析:oop

.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, 這個上一小節進行剖析過相信不會陌生this

拿到pipeline以後再爲其添加handler, 由於channel初始化默認建立的是DefualtChannelPipelinespa

咱們跟到其addLast()方法中:線程

public final ChannelPipeline addLast(ChannelHandler... handlers) { return addLast(null, handlers); }

首先看到這裏的參數實際上是一個可變對象, 也就是能夠傳遞多個handler, 這裏咱們只傳遞了一個3d

咱們繼續跟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的有關的業務邏輯

 

上一節: pipeline的建立

下一節: handler的刪除

相關文章
相關標籤/搜索