適用場景:
- 對於一個請求來說,如果有個物件都有機會處理它,而且不明確到底是哪個物件會處理請求時,我們可以考慮使用責任鏈模式實現它,讓請求從鏈的頭部往後移動,直到鏈上的一個節點成功處理了它為止
優點:
- 傳送者不需要知道自己傳送的這個請求到底會被哪個物件處理掉,實現了傳送者和接受者的解耦
- 簡化了傳送者物件的設計
- 可以動態的新增節點和刪除節點
缺點:
- 所有的請求都從鏈的頭部開始遍歷,對效能有損耗
- 極差的情況,不保證請求一定會被處理
自定義一個責任鏈
在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;
}