Netty源碼解析之Pipeline解析

首先初始化Pipeline如下
創建channel會調用到如下方法
protected AbstractChannel(Channel parent) {
    this.parent = parent;
    id = newId();
    unsafe = newUnsafe();
    pipeline = newChannelPipeline();
}
 
跟蹤newChannelPipeline()源碼如下
// DefaultChannelPipeline.java
protected DefaultChannelPipeline(Channel channel) {
    this.channel = ObjectUtil.checkNotNull(channel, "channel");
    succeededFuture = new SucceededChannelFuture(channel, null);
    voidPromise = new VoidChannelPromise(channel, true);
    // 可見初始化pipeline時,分別實例化了tail和head
    tail = new TailContext(this);
    head = new HeadContext(this);
 
    head.next = tail;
    tail.prev = head;
}
 
跟蹤代碼可看見TailContext與HeadContext均繼承了AbstractChannelHandlerContext(其中HeadContext比TailContext多實現了一個ChannelOutboundHandler 接口,其包含了一些連接、讀寫的方法聲明)
final class TailContext extends AbstractChannelHandlerContext implements ChannelInboundHandler {
 
    TailContext(DefaultChannelPipeline pipeline) {
        super(pipeline, null, TAIL_NAME, true, false);
        setAddComplete();
    }
...................
--
final class HeadContext extends AbstractChannelHandlerContext
implements ChannelOutboundHandler, ChannelInboundHandler {
 
    private final Unsafe unsafe;
 
    HeadContext(DefaultChannelPipeline pipeline) {
        super(pipeline, null, HEAD_NAME, false, true);
        unsafe = pipeline.channel().unsafe();
        setAddComplete();
    }
..................
分析:AbstractChannelHandlerContext的方法結構如下:
Image.png
  • 由上可見定義註冊、解綁、傳播(fireChannelActive)、異常捕獲、讀寫等事件。
總結:以上就是pipeline的簡單初始化過程,下面會繼續深入分析。

添加ChannelHandler
1 首先會調用addLast()方法添加
// DefaultChannelPipeLine.java
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
    final AbstractChannelHandlerContext newCtx;
    // 這裏看出添加channelHandler是同步阻塞的
    synchronized (this) {
        // 1.1 判斷channelHandler是否可被重複添加
        checkMultiplicity(handler);
        // 1.2 創建節點並添加到鏈表
        newCtx = newContext(group, filterName(name, handler), handler);
 
        addLast0(newCtx);
 
        if (!registered) {
            newCtx.setAddPending();
            callHandlerCallbackLater(newCtx, true);
            return this;
        }
        // 1.3 添加用戶回調事件 callHandlerAdded0(newCtx)
        EventExecutor executor = newCtx.executor();
        if (!executor.inEventLoop()) {
            newCtx.setAddPending();
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    callHandlerAdded0(newCtx);
                }
            });
            return this;
        }
    }
    callHandlerAdded0(newCtx);
    return this;
}
 
1.1 是否可被重複復添加 解析
  • 首先跟蹤checkMultiplicity(handler)方法如下:
// DefaultChannelPipeLine.java
private static void checkMultiplicity(ChannelHandler handler) {
    if (handler instanceof ChannelHandlerAdapter) {  // 判斷當前實例類型必須是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.");
        }
        // 被添加過之後會設置標識爲已被添加過
        h.added = true;
    }
}
  • 跟着上面的h.isSharable()看看什麼類型可被重複添加
public boolean isSharable() {
    // 主要邏輯:判斷當前channelHandler是否有Sharable註解,如果有就表示可共享。可被重複添加,反之不行
    Class<?> clazz = getClass();
    Map<Class<?>, Boolean> cache = InternalThreadLocalMap.get().handlerSharableCache();
    Boolean sharable = cache.get(clazz);
    if (sharable == null) {
        sharable = clazz.isAnnotationPresent(Sharable.class);
        cache.put(clazz, sharable);
    }
    return sharable;
}
 
1.2 創建節點並添加到鏈表
  • 首先創建一個channelHandlerContext類
newCtx = newContext(group, filterName(name, handler), handler);
其中filterName(name, handler)會遍歷鏈表檢查當前name是否重複,如果不重複返回(否則拋異常)。
然後通過addLast0(newCtx);添加到鏈表中,其過程如下:
private void addLast0(AbstractChannelHandlerContext newCtx) {
    AbstractChannelHandlerContext prev = tail.prev;
    newCtx.prev = prev;
    newCtx.next = tail;
    prev.next = newCtx;
    tail.prev = newCtx;
}
其大致流程是把newCtx插入到尾結點tail的前面,流程圖如下
Image [1].png
 
1.3 回調添加用戶完成事件 callHandlerAdded0(newCtx),回調之後移除掉
// DefaultChannelPipeline.java
    ........................
    ctx.handler().handlerAdded(ctx);
    ctx.setAddComplete();
} catch (Throwable t) {
    boolean removed = false;
    try {
        remove0(ctx);
........................
  
 
2 channelHandler的刪除
  • channelHandler的刪除與上面類型,直接從鏈表中移除即可,其下是源碼跟蹤
// DefaultChannelPipeline.java
public final ChannelPipeline remove(ChannelHandler handler) {
    remove(getContextOrDie(handler));
    return this;
}
刪除的方法解析如下
private AbstractChannelHandlerContext remove(final AbstractChannelHandlerContext ctx) {
    assert ctx != head && ctx != tail;
 
    synchronized (this) {
        // 2.1 雙向鏈表的方式直接刪除,與上面添加類似
        remove0(ctx);
 
        if (!registered) {
            callHandlerCallbackLater(ctx, false);
            return ctx;
        }
 
        EventExecutor executor = ctx.executor();
        if (!executor.inEventLoop()) {
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    callHandlerRemoved0(ctx);
                }
            });
            return ctx;
        }
    }
    // 2.2 執行刪除成功後的事件回調
    callHandlerRemoved0(ctx);
    return ctx;
}
 

其他:
channelInBound與channelOutBound區別見文章: https://blog.csdn.net/u010013573/article/details/85222110
  • InBound事件順序執行(head->tail),outBound事件逆序執行(tail->head)
 
pipeline還有一個異常鏈,可通過上面的In/OutBound添加對應的異常處理。
  • 異常鏈的傳播順序(tail->head)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章