1. 程式人生 > >Java網路程式設計--Netty中的責任鏈

Java網路程式設計--Netty中的責任鏈

Netty中的責任鏈

設計模式 - 責任鏈模式

責任鏈模式(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的高度可擴充套件性