報道:Dubbo架構設計與源碼解析(三)責任鏈模式

2022-12-23 10:19:06 來源:51CTO博客

作者:周可強

一、責任鏈模式簡介

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  Invoker buildInvokerChain(final Invoker invoker, String key, String group) {        Invoker last = invoker;        // 獲得過濾器數組 (已經排好序的)        List 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 next = last;                last = new Invoker() {                    @Override                    public Class 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 getActivateExtension(URL url, String key, String group) {        // 從 Dubbo URL 獲得參數值        String value = 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"` 。例如, ,代表移除所有默認過濾器。        if (!names.contains(Constants.REMOVE_VALUE_PREFIX + Constants.DEFAULT_KEY)) {            // 獲得拓展實現類數組            getExtensionClasses();            // 循環            for (Map.Entry 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) // 判斷是否配置移除。例如 ,則 MonitorFilter 會被移除                            && isActive(activate, url)) { // 判斷是否激活                        exts.add(ext);                    }                }            }            // 排序            Collections.sort(exts, ActivateComparator.COMPARATOR);        }        // 處理自定義配置的拓展對象們。例如在  ,代表需要加入 DemoFilter        List usrs = new ArrayList();        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)) { // 判斷非移除的                // 將配置的自定義在自動激活的拓展對象們前面。例如, ,則 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.classpublic 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做成了可插拔的,減少對代碼的侵入性,這點是非常值得我們學習的。

標簽: 請求處理 設計模式 不能保證

上一篇:#yyds干貨盤點# LeetCode程序員面試金典:特定深度節點鏈表
下一篇:openssl-1.1.1n安裝