Netty 職責鏈Pipeline詳解

1. 設計模式 - 責任鏈模式

責任鏈模式(Chain of Responsibility Pattern) 為請求創建了一個處理對象的鏈。

發起請求和距離處理請求的過程進行解耦:職責鏈上的處理者負責處理請求,客戶只需要將請求發送到職責鏈上即可,無需關心請求的處理細節和請求的傳遞。

2. 實現責任鏈模式

實現責任鏈模式的 4個要素

  • 處理器抽象類
  • 具體的處理器實現類
  • 保存處理器信息
  • 處理執行
  • 代碼的例子

// -----鏈表形式調用------netty就是類似的這種形式
public class PipelineDemo {
    /**
     * 初始化的時候造一個head,作為責任鏈的開始,但是并沒有具體的處理
     */
    public HandlerChainContext head = new HandlerChainContext(new AbstractHandler() {
        @Override
        void doHandler(HandlerChainContext handlerChainContext, Object arg0) {
            handlerChainContext.runNext(arg0);
        }
    });

    public void requestProcess(Object arg0) {
        this.head.handler(arg0);
    }

    public void addLast(AbstractHandler handler) {
        HandlerChainContext context = head;
        while (context.next != null) {
            context = context.next;
        }
        context.next = new HandlerChainContext(handler);
    }


    public static void main(String[] args) {
        PipelineDemo pipelineChainDemo = new PipelineDemo();
        pipelineChainDemo.addLast(new Handler2());
        pipelineChainDemo.addLast(new Handler1());
        pipelineChainDemo.addLast(new Handler1());
        pipelineChainDemo.addLast(new Handler2());

        // 發起請求
        pipelineChainDemo.requestProcess("火車嗚嗚嗚~~");

    }
}

/**
 * handler上下文,我主要負責維護鏈,和鏈的執行
 */
class HandlerChainContext {
    HandlerChainContext next; // 下一個節點
    AbstractHandler handler;

    public HandlerChainContext(AbstractHandler handler) {
        this.handler = handler;
    }

    void handler(Object arg0) {
        this.handler.doHandler(this, arg0);
    }

    /**
     * 繼續執行下一個
     */
    void runNext(Object arg0) {
        if (this.next != null) {
            this.next.handler(arg0);
        }
    }
}

// 處理器抽象類
abstract class AbstractHandler {
    /**
     * 處理器,這個處理器就做一件事情,在傳入的字符串中增加一個尾巴..
     */
    abstract void doHandler(HandlerChainContext handlerChainContext, Object arg0); // handler方法
}

// 處理器具體實現類
class Handler1 extends AbstractHandler {
    @Override
    void doHandler(HandlerChainContext handlerChainContext, Object arg0) {
        arg0 = arg0.toString() + "..handler1的小尾巴.....";
        System.out.println("我是Handler1的實例,我在處理:" + arg0);
        // 繼續執行下一個
        handlerChainContext.runNext(arg0);
    }
}

// 處理器具體實現類
class Handler2 extends AbstractHandler {
    @Override
    void doHandler(HandlerChainContext handlerChainContext, Object arg0) {
        arg0 = arg0.toString() + "..handler2的小尾巴.....";
        System.out.println("我是Handler2的實例,我在處理:" + arg0);
        // 繼續執行下一個
        handlerChainContext.runNext(arg0);
    }
}

本質來說,就是定義了一個抽象類,然后具體的處理類實現了這個抽象類中定義的方法,如 AbstractHandler 中的 doHandler 方法。而責任鏈即表示是一個鏈式結構,上方的HandlerChainContext 正是起到了這個類似鏈表的效果,這樣的話,就起到了職責“鏈”的效果了。 即每次執行完HandlerChainContext的AbstractHandler的實現類之后,會判斷一下當前的HandlerChainContext 是否為空,不為空則繼續執行下一個HandlerChainContext的AbstractHandler的實現類,以此類推。


3. Netty中的ChannelPipeline責任鏈

Netty中的ChannelPipeline責任鏈

4. 入站事件和出站事件

入站事件: 通常只I/O 線程生成入站數據。
(通俗理解:從socket底層自己往上冒上來的事件都是入站)
比如EventLoop收到selector的OP_READ事件,入站處理器調用socketChannel.read(ByteBuffer)接收到數據后,這將導致通道的ChannelPipeline中包含的下一個中的channelRead方法被調用。

出站事件:經常是指I/O 線程執行實際的輸出操作。
(通俗理解:想主動往socket底層操作的事件的都是出站)
比如bind方法用意是請求server socket綁定到給定的SocketAddress,這將導致通道的ChannelPipeline中包含的下一個出站處理器中的bind方法被調用。


5. Netty 中事件的定義

  • inbound 入站事件


    入站事件
  • outbound 出站事件

出站事件

6. Pipeline中的handler是什么

  • ChannelHandler:用于處理I/O事件或攔截I/O操作,并轉發到ChannelPipeline中的下一個處理器。
    這個頂級接口定義功能很弱,實際使用時會去實現下面兩大子接口:
    處理入站I/O事件的ChannelInboundHandler、處理出站I/O操作的ChannelOutboundHandler

  • 適配器類:為了開發方便,避免所有handler去實現一邊接口方法,Netty提供了簡單的實現類:
    1.ChannelInboundHandlerAdapter處理入站I/O事件
    2.ChannelOutboundHandlerAdapter來處理出站I/O操作
    3.ChannelDuplexHandler來支持同時處理入站和出站事件

  • ChannelHandlerContext:實際存儲在Pipeline中的對象并非ChannelHandler,而是上下文對象。
    將handler,包裹在上下文對象中,通過上下文對象與它所屬的ChannelPipeline交互,向上或向下傳遞事件或者修改pipeline都是通過上下文對象。

7. 維護Pipeline中的handler

ChannelPipeline是線程安全的,ChannelHandler可以在任何時候添加或刪除。
例如,你可以在即將交換敏感信息時插入加密處理程序,并在交換后刪除它。
一般操作,初始化的時候增加進去,較少刪除。下面是Pipeline中管理handler的API

Pipeline中管理handler的API

8. Handler 的執行分析

假設當前的ChannelPipeline中有以下幾個Handler

正常來說,入站事件是由下到上的執行順序,出站事件是由上到下的順序


9. 分析registered 入站事件的處理


10. 分析bind出站事件的處理


11. 分析accept入站事件的處理

這是一個分配的過程,main Group負責accept,然后分配sub Group負責read


12. 分析read入站事件的處理

pipeline分析的關鍵4要素:什么事件、有哪些處理器、哪些會被觸發、執行順序


小結

用戶在管道中有一個或多個channelhandler來接收I/O事件(例如讀取)和請求I/O操作(例如寫入和關閉)。

一個典型的服務器在每個通道的管道中都有以下處理程序,但是根據協議和業務邏輯的復雜性和特征,可能會有所不同:

  • 協議解碼器 - 將二進制數據(例如ByteBuf)轉換為Java對象。
  • 協議編碼器 - 將Java對象轉換為二進制數據。
  • 業務邏輯處理程序 - 執行實際的業務邏輯(例如數據庫訪問)。

責任鏈設計模式的運用,保證了Netty的高度可拓展性!


如果覺得有收獲就點個贊吧,更多知識,請點擊關注查看我的主頁信息哦~

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。