Java網絡編程–Netty中的責任鏈
- 2019 年 10 月 7 日
- 筆記
設計模式 – 責任鏈模式
責任鏈模式(Chain of Responsibility Pattern)是一種是行為型設計模式,它為請求創建了一個處理對象的鏈。其鏈中每一個節點都看作是一個對象,每個節點處理的請求均不同,且內部自動維護一個下一節點對象。當一個請求從鏈式的首端發出時,會沿着鏈的路徑依次傳遞給每一個節點對象,直至有對象處理這個請求為止。
責任鏈模式主要解決了發起請求和具體處理請求的過程解耦,職責鏈上的處理者負責處理請求,用戶只需將請求發送到職責鏈上即可,無需關心請求的處理細節和請求的傳遞。

責任鏈模式的實現
責任鏈模式的實現主要有四個要素:處理器抽象類,具體的處理器實現類,保存處理器信息,處理執行。
偽代碼示例:
// *** 集合形式存儲 類似tomcat中的filters *** // 處理器抽象類 class AbstractHandler{void doHandler(Object arg0)} // 處理器具體實現類 class Handler1 extends AbstractHandler{assert coutine;} class Handler2 extends AbstractHandler{assert coutine;} class Handler3 extends AbstractHandler{assert coutine;} // 創建集合併存儲所有處理器實例信息 List handlers = new List(); handlers.add(handler1, handler2, handler3); // 處理請求,調用處理器 void process(request){ for(handler in handlers){ handler.doHandler(request); } } // 發起請求調用,通過責任鏈處理請求 call.process(request); // *** 鏈表形式調用 netty中的用法 *** // 處理器抽象類 class AbstractHandler{ AbstractHandler next;//下一節點 void doHandler(Object arg0) } // 處理器具體實現類 class Handler1 extends AbstractHandler{assert coutine;} class Handler2 extends AbstractHandler{assert coutine;} class Handler3 extends AbstractHandler{assert coutine;} // 將處理器串成鏈表存儲 pipeline = 頭[handler1 -> handler2 -> handler3]尾; // 處理請求,從頭到尾調用處理器 void process(request){ handler = pipeline.findOne; //查找第一個 while(handler != null){ handler.doHandler(request); handler = handler.next; } }
鏈表形式的責任鏈實現的具體代碼示例:
// 鏈表形式調用 netty中的用法 public class PipelineDemo { // 初始化的時候造一個head,作為責任鏈的開始,但是並沒有具體的處理 public HandlerChainContext head = new HandlerChainContext( new AbstractHandler() { @Override void doHandler(HandlerChainContext handlerChainContext, Object arg0) { handlerChainContext.runNext(arg0); } }); public void processRequest(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.processRequest("火車嗚嗚嗚~~"); } } // 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); } // 處理器具體實現類 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); } } // 輸出結果:我是Handler2的實例,我在處理:火車嗚嗚嗚~~..handler2的小尾巴..... 我是Handler1的實例,我在處理:火車嗚嗚嗚~~..handler2的小尾巴.......handler1的小尾巴..... 我是Handler1的實例,我在處理:火車嗚嗚嗚~~..handler2的小尾巴.......handler1的小尾巴.......handler1的小尾巴..... 我是Handler2的實例,我在處理:火車嗚嗚嗚~~..handler2的小尾巴.......handler1的小尾巴.......handler1的小尾巴.......handler2的小尾巴.....
Netty中的ChannelPipeline責任鏈
pipeline管道保存了通道所有處理器信息,創建channel時自動創建一個專有的pipeline,入站事件和出站事件會調用pipeline上的處理器

入站事件和出站事件
入站事件:通常指IO線程生成了入站數據
(通俗理解:從socket底層自己往上冒上來的事件都是入站) 比如EventLoop收到selector的OP_READ事件,入站處理器調用socketChannel.read(ByteBuffer)接受到數據後,這將導致通道的ChannelPipeline中包含的下一個中的channelRead方法被調用
出站事件:通常指IO線程執行實際的輸出操作
(通俗理解:想主動往socket底層操作的事件的都是出站) 比如bind方法用意是請求server socket綁定到給定的SocketAddress,這將導致通道的ChannelPipeline中包含的下一個出站處理器中的bind方法被調用

Pipeline中的handler
ChannelHandler:用於處理IO事件或攔截IO操作,並轉發到ChannelPipeline中的下一個處理器。這個頂級接口定義功能很弱,事件使用時會實現下面兩大子接口:處理入站IO事件的ChannelInBoundHandler,處理出站IO事件的ChannelOutBoundHandler
適配器:為了開發的方便,避免所有的handler去實現一遍接口方法,Netty提供了簡單的實現類:
ChannelInBoundHandlerAdapter處理入站IO事件, ChannelOutBoundHandlerAdapter處理出站IO事件, ChannelDuplexHandler支持同時處理入站和出站事件
ChannelHandlerContext:實際存儲在Pipeline中的對象並非ChannelHandler,而是上下文對象,將handler包裹在上下文對象中,通過上下文屬的ChannelPipeline交互,向上或向下傳遞事件或者修改pipeline都是通過上下文對象。
ChannelPipeline是線程安全的,ChannelHandler可以在任何時候添加或刪除。 例如,可以在即將交換敏感信息時插入加密處理程序,並在交換後刪除。 一般操作,初始化的時候增加進去,較少刪除。
Pipeline中管理handler的API:

handler的執行分析

分析register入站事件的處理

分析bind出站事件的處理

分析accept入站事件的處理

分析read入站事件的處理

小結
用戶在管道中有一個或多個channelhandler來接受IO事件和請求IO操作 一個典型的服務器會在每個通道的管道中都有以下處理程序,但是根據協議和業務邏輯的複雜性和特徵,可能會有所不同: 協議解碼器 – 將二進制數據轉換為Java對象 協議編碼器 – 將Java對象轉換成二進制數據 業務邏輯處理器 – 執行實際的業務邏輯 責任鏈模式的運用,保證了Netty的高度可擴展性