作者:周可強
一、責任鏈模式簡介
1、責任鏈模式定義
責任鏈(Chain of Responsibility)模式的定義:為了避免請求發送者與多個請求處理者耦合在一起,于是將所有請求的處理者通過前一物件記住其下一個物件的參考而連成一條鏈;當有請求發生時,可將請求沿著這條鏈傳遞,直到有物件處理它為止,在責任鏈模式中,客戶只需要將請求發送到責任鏈上即可,無須關心請求的處理細節和請求的傳遞程序,請求會自動進行傳遞,所以責任鏈將請求的發送者和請求的處理者解耦了,
2、責任鏈特點
責任鏈模式是一種物件行為型模式,
其主要優點如下,
1).降低了物件之間的耦合度,該模式使得一個物件無須知道到底是哪一個物件處理其請求以及鏈的結構,發送者和接收者也無須擁有對方的明確資訊,
2).增強了系統的可擴展性,可以根據需要增加新的請求處理類,滿足開閉原則,
3).增強了給物件指派職責的靈活性,當作業流程發生變化,可以動態地改變鏈內的成員或者調動它們的次序,也可動態地新增或者洗掉責任,責任鏈簡化了物件之間的連接,每個物件只需保持一個指向其后繼者的參考,不需保持其他所有處理者的參考,這避免了使用眾多的 if 或者 if···else 陳述句,
4).責任分擔,每個類只需要處理自己該處理的作業,不該處理的傳遞給下一個物件完成,明確各類的責任范圍,符合類的單一職責原則,
其主要缺點如下,
1).不能保證每個請求一定被處理,由于一個請求沒有明確的接收者,所以不能保證它一定會被處理,該請求可能一直傳到鏈的末端都得不到處理,
2).對比較長的職責鏈,請求的處理可能涉及多個處理物件,系統性能將受到一定影響,
3).職責鏈建立的合理性要靠客戶端來保證,增加了客戶端的復雜性,可能會由于職責鏈的錯誤設定而導致系統出錯,如可能會造成回圈呼叫,
3、責任鏈結構圖
二、Dubbo中的責任鏈模式
1、過濾器日志
通過列印過濾器的日志,我們可以看到在發布服務的程序中,會依次經過dubbo的每個過濾器類,以此來保證服務的完善,
2、過濾器簡圖
dubbo通過將每個過濾器類filter封裝成dubbo的核心模型invoker進行組裝,最終形成晚上的過濾器責任鏈filterChain,
3、過濾器類圖
Protocol是核心模型invoker暴露和參考的主功能入口,采用SPI的介面,他的兩個方法export和refer分別對應provider和consumer端的服務功能,ProtocolFilterWapper則是Dubbo的過濾器的主要實作類,通過重寫的export和refer指向buildInvokerChain方法,在buildInvokerChain中進行責任鏈的獲取與組裝,在extensionLoader中通過SPI獲取Filter的各實作類,并通過ActivateComparator進行排序,最終形成完整的責任鏈,
三、Dubbo中各Filter責任介紹
1、provider用到的filter
2、consumer用到的filter
四、原始碼探析
進入到核心類ProtocolFilterWrapper中,在實作類中export和refer,都采用相同的構造責任鏈方法buildInvokerChain,只是通過引數group進行區分
在buildInvokerChain中,通過getActivateExtension獲取過濾器陣列,并在之后封裝成核心模型invoker并組裝成責任鏈
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
Invoker<T> last = invoker;
// 獲得過濾器陣列 (已經排好序的)
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
// 創建帶 Filter 鏈的 Invoker 物件
if (!filters.isEmpty()) {
for (int i = filters.size() - 1; i >= 0; i--) {
final Filter filter = filters.get(i);
final Invoker<T> next = last;
last = new Invoker<T>() {
@Override
public Class<T> getInterface() {
return invoker.getInterface();
}
@Override
public URL getUrl() {
return invoker.getUrl();
}
@Override
public boolean isAvailable() {
return invoker.isAvailable();
}
@Override
public Result invoke(Invocation invocation) throws RpcException {
return filter.invoke(next, invocation);
}
@Override
public void destroy() {
invoker.destroy();
}
@Override
public String toString() {
return invoker.toString();
}
};
}
}
System.out.println("group:" + group);
for (Filter filter : filters) {
System.out.println(filter.getClass());
}
return last;
}
getActivateExtension是主要的組裝邏輯,他包含獲取與排序等邏輯
首先進行判斷是否采用系統默認的Filter過濾器,并對每一個系統過濾器進行校驗是否移除,然后對系統過濾器排序,再通過指定的引數,增加用戶自定義的過濾器組裝責任鏈
public List<T> getActivateExtension(URL url, String key, String group) {
// 從 Dubbo URL 獲得引數值
String value = https://www.cnblogs.com/Jcloud/p/url.getParameter(key);
// 獲得符合自動激活條件的拓展物件陣列
return getActivateExtension(url, value == null || value.length() == 0 ? null : Constants.COMMA_SPLIT_PATTERN.split(value), group);
}
public List getActivateExtension(URL url, String[] values, String group) {
List exts = new ArrayList();
//所有用戶自己配置的filter資訊(有些Filter是默認激活的,有些是配置激活的,這里的names就指的配置激活的filter資訊)
List names = values == null ? new ArrayList(0) : Arrays.asList(values);
// 處理自動激活的拓展物件們
// 判斷不存在配置 `"-name"` ,例如,<dubbo:service filter="-default" /> ,代表移除所有默認過濾器,
if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {
// 獲得拓展實作類陣列
getExtensionClasses();
// 回圈
for (Map.Entry<String, Activate> entry : cachedActivates.entrySet()) {
//name指的是SPI讀取的組態檔的key
String name = entry.getKey();
Activate activate = entry.getValue();
if (isMatchGroup(group, activate.group())) { // 匹配分組
// 獲得拓展物件
T ext = getExtension(name);
if (!names.contains(name) // 不包含在自定義配置里,如果包含,會在下面的代碼處理,
&& !names.contains(Constants.REMOVE_VALUE_PREFIX + name) // 判斷是否配置移除,例如 <dubbo:service filter="-monitor" />,則 MonitorFilter 會被移除
&& isActive(activate, url)) { // 判斷是否激活
exts.add(ext);
}
}
}
// 排序
Collections.sort(exts, ActivateComparator.COMPARATOR);
}
// 處理自定義配置的拓展物件們,例如在 <dubbo:service filter="demo" /> ,代表需要加入 DemoFilter
List<T> usrs = new ArrayList<T>();
for (int i = 0; i < names.size(); i++) {
String name = names.get(i);
if (!name.startsWith(Constants.REMOVE_VALUE_PREFIX) && !names.contains(Constants.REMOVE_VALUE_PREFIX + name)) { // 判斷非移除的
// 將配置的自定義在自動激活的拓展物件們前面,例如,<dubbo:service filter="demo,default,demo2" /> ,則 DemoFilter 就會放在默認的過濾器前面,
if (Constants.DEFAULT_KEY.equals(name)) {
if (!usrs.isEmpty()) {
exts.addAll(0, usrs);
usrs.clear();
}
} else {
// 獲得拓展物件
T ext = getExtension(name);
usrs.add(ext);
}
}
}
// 添加到結果集
if (!usrs.isEmpty()) {
exts.addAll(usrs);
}
return exts;
}
系統默認的過濾器和udf過濾器進行區分
以ContextFilter為例,系統默認過濾器包含Activate注解,用于指定所屬分組與排序權重,用戶自己實作的過濾器則不能添加Activate注解通過發布時指定所需的過濾器
我們看下具體的排序比較方法,首先判斷Activate注解是否指定before和after引數用來指定排序,若不存在則采用order權重進行排序
ActivateComparator.class
public int compare(Object o1, Object o2) {
// 基本排序
if (o1 == null && o2 == null) {
return 0;
}
if (o1 == null) {
return -1;
}
if (o2 == null) {
return 1;
}
if (o1.equals(o2)) {
return 0;
}
Activate a1 = o1.getClass().getAnnotation(Activate.class);
Activate a2 = o2.getClass().getAnnotation(Activate.class);
// 使用注解的 `after` 和 `before` 屬性,排序
if ((a1.before().length > 0 || a1.after().length > 0 || a2.before().length > 0 || a2.after().length > 0) // (a1 或 a2) 存在 (`after` 或 `before`) 屬性,
&& o1.getClass().getInterfaces().length > 0 && o1.getClass().getInterfaces()[0].isAnnotationPresent(SPI.class)) { // 實作的介面,有 @SPI 注解,
// 獲得拓展加載器
ExtensionLoader<?> extensionLoader = ExtensionLoader.getExtensionLoader(o1.getClass().getInterfaces()[0]);
// 以 a1 的視角,進行一次比較
if (a1.before().length > 0 || a1.after().length > 0) {
String n2 = extensionLoader.getExtensionName(o2.getClass());
for (String before : a1.before()) {
if (before.equals(n2)) {
return -1;
}
}
for (String after : a1.after()) {
if (after.equals(n2)) {
return 1;
}
}
}
// 以 a2 的視角,進行一次比較,
if (a2.before().length > 0 || a2.after().length > 0) {
String n1 = extensionLoader.getExtensionName(o1.getClass());
for (String before : a2.before()) {
if (before.equals(n1)) {
return 1;
}
}
for (String after : a2.after()) {
if (after.equals(n1)) {
return -1;
}
}
}
}
// 使用注解的 `order` 屬性,排序,
int n1 = a1 == null ? 0 : a1.order();
int n2 = a2 == null ? 0 : a2.order();
// never return 0 even if n1 equals n2, otherwise, o1 and o2 will override each other in collection like HashSet
return n1 > n2 ? 1 : -1;
}
總結:
責任鏈模式是設計模式中簡單且常見的設計模式,可能我們日常中也會經常應用責任鏈模式,dubbo中的責任鏈模式將靈活性發揮的很充分,不論是從分組概念、通過注解指定排序的優先級、每個filter的是否移除 等,將每個filter做成了可插拔的,減少對代碼的侵入性,這點是非常值得我們學習的,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/540652.html
標籤:架構設計
上一篇:23種設計模式,你學廢了嘛?
