Netty中的責任鏈模式

賜我白日夢發表於2019-07-24

適用場景:

  • 對於一個請求來說,如果有個物件都有機會處理它,而且不明確到底是哪個物件會處理請求時,我們可以考慮使用責任鏈模式實現它,讓請求從鏈的頭部往後移動,直到鏈上的一個節點成功處理了它為止

優點:

  • 傳送者不需要知道自己傳送的這個請求到底會被哪個物件處理掉,實現了傳送者和接受者的解耦
  • 簡化了傳送者物件的設計
  • 可以動態的新增節點和刪除節點

缺點:

  • 所有的請求都從鏈的頭部開始遍歷,對效能有損耗
  • 極差的情況,不保證請求一定會被處理

自定義一個責任鏈

在java中不再存在指標了,如果我們想建立一個連結串列,只能是在本類中新增本類屬性, 因為我們想建立一個連結串列,所以這是必須的工作

需要提供set方法,讓當前的節點可以設定自己的下一個節點

處理請求的邏輯,設計成抽象方法,讓不同的節點根據自己的需求去實現

public abstract class Approver {
Approver approver;
String name;
// todo 抽象父類中可以存在建構函式,但是當我們建立子類時,必須要有一個引數的建構函式,
// todo 讓子類一個引數的建構函式,來給這個函式初始化
public Approver (String name){
    this.name=name;
}

public abstract void ProcessRequest(PurchaseRequest request);

// 如果當前的處理器處理不了,就會往下傳播
public void setApprover( Approver approver){
    this.approver=approver;
}
}

PurchaseRequest,需要被處理的請求,根據自己的需要各不相同

接著就是連結串列上的不同功能的節點都要實現上面的抽象類Approver,重寫它的抽象方法,新增上他們特定的功能

測試:

    // 建立出各個節點
    Approver1 approver1 = new Approver1();
    Approver2 approver2 = new Approver2();
    Approver3 approver3 = new Approver3();

    // 設定他們關係
    approver1.setApprover(approver2);
    approver2.setApprover(approver3);

    // 發起請求
    Client client = new Client();
    PurchaseRequest purchaseRequest = client.sendRequest();

    // 處理請求
    tom.ProcessRequest(purchaseRequest);

把請求傳遞給責任鏈的第一個節點,她會自動往後傳播下去,直到有一個節點成功處理了它

Netty的責任鏈設計

netty的pipeline設計,就採用了責任鏈設計模式, 底層採用雙向連結串列的資料結構, 將鏈上的各個處理器串聯起來

客戶端每一個請求的到來,netty都認為,pipeline中的所有的處理器都有機會處理它,因此,對於入棧的請求,全部從頭節點開始往後傳播,一直傳播到尾節點(來到尾節點的msg會被釋放掉)

netty的責任鏈模式中的元件

  • 責任處理器介面
    • pipeline中的處理器都它的具體實現
  • 新增刪除責任處理器的介面
  • 上下文
    • 通過這個上下文,可以獲得需要的資料,屬性
  • 責任終止機制
    • pipeline中的每一個節點,都可以終止事件的傳播

netty的責任處理器介面

責任處理器介面, pipeline中的所有的handler的頂級抽象介面,它規定了所有的handler統一要有新增,移除,異常捕獲的行為

public interface ChannelHandler {

// todo 當handler被新增到真實的上下文中,並且準備處理事件時被呼叫
// todo handler 被新增進去的回撥
void handlerAdded(ChannelHandlerContext ctx) throws Exception;

//  todo 是 handler 被移出的後的 回撥
void handlerRemoved(ChannelHandlerContext ctx) throws Exception;

@Deprecated
void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;

@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Sharable {
    // no value
}
}

netty對責任處理介面,做了更細粒度的劃分, 處理器被分成了兩種, 一種是站處理器ChannelInboundHandler,另一種是出站處理器ChannelOutboundHandler,這兩個介面都繼承自ChannelHandler

新增刪除責任處理器的介面

netty中所有的處理器最終都在新增在pipeline上,所以,新增刪除責任處理器的介面的行為 netty在channelPipeline中的進行了規定

public interface ChannelPipeline
        extends ChannelInboundInvoker, ChannelOutboundInvoker, Iterable<Entry<String, ChannelHandler>> {

    ChannelPipeline addFirst(String name, ChannelHandler handler);

    ChannelPipeline addFirst(EventExecutorGroup group, String name, ChannelHandler handler);

    ChannelPipeline addLast(String name, ChannelHandler handler);

    ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler);

    ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler);

 ...

上下文

pipeline中的handler被封裝進了上下文中,如下, 通過上下文,可以輕鬆拿到當前節點所屬的channel, 以及它的執行緒執行器

// todo AttributeMap -- 讓ChannelHandlerContext 可以儲存自定義的屬性
// todo ChannelInboundInvoker -- 讓ChannelHandlerContext 可以進行 InBound事件的傳播,讀事件,read 或者是  註冊事件 active事件
// todo ChannelOutboundInvoker -- 讓ChannelHandlerContext 可以傳播寫事件
public interface ChannelHandlerContext extends AttributeMap, ChannelInboundInvoker, ChannelOutboundInvoker {
 
    // todo 獲取ChannelHandlerContext所對應的這個Channel物件
    Channel channel();

    // todo 獲取事件執行器
    EventExecutor executor();
...

責任終止機制

責任終止機制

  • 在pipeline中的任意一個節點,只要我們不手動的往下傳播下去,這個事件就會終止傳播在當前節點
  • 對於入站資料,預設會傳遞到尾節點,進行回收,如果我們不進行下一步傳播,事件就會終止在當前節點,別忘記回收msg
  • 對於出站資料,用header節點的使用unsafe物件,把資料寫會客戶端也意味著事件的終止

事件的傳播

底層事件的傳播使用的就是針對連結串列的操作

private AbstractChannelHandlerContext findContextInbound() {
    AbstractChannelHandlerContext ctx = this;
    do {
        ctx = ctx.next;
    } while (!ctx.inbound);
    return ctx;
}

相關文章