Dubbo的Filter鏈梳理---分組可見和順序調整

 

 

前言:
  剛剛寫了篇博文: Dubbo透傳traceId/logid的一種思路, 對dubbo的filter機制有了一個直觀的理解. 同時對filter也多了一些好奇心, 好奇filter鏈是如何組織的, 它的順序是否支持調整. 帶着這些疑問, 同時也是趁熱打鐵, 讓咱們一塊兒來簡單梳理下.css

 

寫下疑惑:
  其實網上有一篇文章: Dubbo Filter詳解, 寫的很是好, 基本上把Dubbo的Filter鏈的組織, 順序性, 自定義順序的方式, 說的很清楚了. 這邊請容許我再作一次知識的搬運工, ^_^.
  同時讓我引入幾個疑問, 帶着疑問去解讀代碼, 可能效果更好.
  1. 註解@Activate是不是Dubbo Filter必須的, 其上的group和order分別扮演什麼樣的角色?
  讓咱們貼一下ConsumerContextFilter的類定義html

@Activate(
    group = {"consumer"},
    order = -10000
)
public class ConsumerContextFilter implements Filter {

}

  2. Filter的順序是否能夠調整, 如何實現?
  這裏面又能夠分好幾個小問題, 好比默認的filter是按什麼標準排序的, 如何調整自定義filter和自帶filter的順序, 甚至去掉自帶filter.
  在Dubbo透傳TraceId的實踐中, 就發現自定義的filter是在系統自帶filter後執行的, 可是我想調整順序, 卻發現無從入手, T_T.java

 

源碼解讀:
  Dubbo的Filter鏈構造的入口是在ProtocolFilterWrapper類裏.閉包

public class ProtocolFilterWrapper implements Protocol {

    private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        
        final Invoker last = invoker;
        // *) 得到全部激活的Filter(已經排好序的)
        List filters = ExtensionLoader.getExtensionLoader(Filter.class)
        		.getActivateExtension(invoker.getUrl(), key, group);

        if(filters.size() > 0) {
           // *) 以很是規的方式--閉包, 構建了filter鏈
            for(int i = filters.size() - 1; i >= 0; --i) {
                final Filter filter = (Filter)filters.get(i);
                last = new Invoker() {
                    public Class<T> getInterface() {
                        return invoker.getInterface();
                    }

                    public URL getUrl() {
                        return invoker.getUrl();
                    }

                    public boolean isAvailable() {
                        return invoker.isAvailable();
                    }

                    public Result invoke(Invocation invocation) throws RpcException {
                        return filter.invoke(last, invocation);
                    }

                    public void destroy() {
                        invoker.destroy();
                    }

                    public String toString() {
                        return invoker.toString();
                    }
                };
            }
        }

        return last;
    }

}

  而在具體的獲取激活的filter列表的代碼時app

public List<T> getActivateExtension(URL url, String[] values, String group) {
    ArrayList exts = new ArrayList();
    // 全部用戶本身配置的filter信息(有些Filter是默認激活的,有些是配置激活的,這裏這裏的names就指的配置激活的filter信息)
    Object names = values == null?new ArrayList(0):Arrays.asList(values);
    String name;
    // 配置指定的項包含'-default'時, 則不加載默認的filter鏈組, 反之則加載
    if(!((List)names).contains("-default")) {
        this.getExtensionClasses();
        Iterator usrs = this.cachedActivates.entrySet().iterator();

        while(usrs.hasNext()) {
            Entry i = (Entry)usrs.next();
            name = (String)i.getKey();
            Activate ext = (Activate)i.getValue();
            // group的取值範圍限於provider/consumer, 標明做用的場景
            if(this.isMatchGroup(group, ext.group())) {
                Object ext1 = this.getExtension(name);
                // 這裏以Filter爲例:三個判斷條件的含義依次是:
                // 1.用戶配置的filter列表中不包含當前ext
                // 2.用戶配置的filter列表中不包含當前ext的加-的key
                // 3.若是用戶的配置信息(url中體現)中有能夠激活的配置key而且數據不爲0,false,null,N/A,也就是說有正常的使用
                if(!((List)names).contains(name) && !((List)names).contains("-" + name) && this.isActive(ext, url)) {
                    exts.add(ext1);
                }
            }
        }
        // 根據Activate註解上的order排序, 有點相似css的zindex屬性, 數值越小排在前面
        Collections.sort(exts, ActivateComparator.COMPARATOR);
    }
	// 進行到此步驟的時候Dubbo提供的原生的Filter已經被添加完畢了,下面處理用戶本身擴展的Filter
    ArrayList var11 = new ArrayList();

    for(int var12 = 0; var12 < ((List)names).size(); ++var12) {
        name = (String)((List)names).get(var12);
        // 該項不是 剔除項(以'-'開頭)時, 進入添加環節
        if(!name.startsWith("-") && !((List)names).contains("-" + name)) {
          	// 能夠經過default關鍵字替換Dubbo原生的Filter鏈,主要用來控制調用鏈順序
            if("default".equals(name)) {
                if(var11.size() > 0) {
               		// 加入用戶本身定義的擴展Filter
                    exts.addAll(0, var11);
                    var11.clear();
                }
            } else {
                Object var13 = this.getExtension(name);
                var11.add(var13);
            }
        }
    }

    if(var11.size() > 0) {
        exts.addAll(var11);
    }

    return exts;
}

  經過簡單的配置'-'能夠手動剔除Dubbo原生的Filter經過default表明Dubbo原生的Filter子鏈, 經過配置指定從而實現filter鏈的順序控制. 這大概這段代碼能夠解讀出的核心思想.ide

  默認filter鏈, 先執行原生filter, 再依次執行自定義filter, 繼而回溯到原點.學習

  Dubbo原生的filter定義在META-INF/dubbo/internal/com.alibaba.dubbo.rpc.filter文件中, 具體以下:ui

echo=com.alibaba.dubbo.rpc.filter.EchoFilter
generic=com.alibaba.dubbo.rpc.filter.GenericFilter
genericimpl=com.alibaba.dubbo.rpc.filter.GenericImplFilter
token=com.alibaba.dubbo.rpc.filter.TokenFilter
accesslog=com.alibaba.dubbo.rpc.filter.AccessLogFilter
activelimit=com.alibaba.dubbo.rpc.filter.ActiveLimitFilter
classloader=com.alibaba.dubbo.rpc.filter.ClassLoaderFilter
context=com.alibaba.dubbo.rpc.filter.ContextFilter
consumercontext=com.alibaba.dubbo.rpc.filter.ConsumerContextFilter
exception=com.alibaba.dubbo.rpc.filter.ExceptionFilter
executelimit=com.alibaba.dubbo.rpc.filter.ExecuteLimitFilter
deprecated=com.alibaba.dubbo.rpc.filter.DeprecatedFilter
compatible=com.alibaba.dubbo.rpc.filter.CompatibleFilter
timeout=com.alibaba.dubbo.rpc.filter.TimeoutFilter
monitor=com.alibaba.dubbo.monitor.support.MonitorFilter
validation=com.alibaba.dubbo.validation.filter.ValidationFilter
cache=com.alibaba.dubbo.cache.filter.CacheFilter
trace=com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter
future=com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter

 

解答疑惑:
  經過源碼的解讀, 咱們來嘗試回答一下開頭設定的兩個問題.
  1. 註解@Activate是不是Dubbo Filter必須的, 其上的group和order分別扮演什麼樣的角色?
  對於Dubbo原生自帶的filter, 註解@Activate是必須, 其group用於provider/consumer的站隊, 而order值是filter順序的依據. 可是對於自定義filter而言, 註解@Activate沒被用到, 其分組和順序, 徹底由用戶手工配置指定. 若是自定義filter添加了@Activate註解, 並指定了group了, 則這些自定義filter將升級爲原生filter組.
  2. Filter的順序是否能夠調整, 如何實現?
  能夠調整, 經過'-'符號能夠去除某些filter, 而default表明默認激活的原生filter子鏈, 經過重排default和自定義filter的順序, 達到實現順序控制的目的.this

 

案例實戰:
  讓咱們來構建幾個case, 來看看如何配置能知足.
  假定自定義filter的對象爲filter1, filter2
  case 1: 其執行順序爲, 原生filter子鏈->filter1->filter2url

<dubbo:reference filter="filter1,filter2"/>

  case 2: 其執行順序爲, filter1->filter2->原生filter子鏈

<dubbo:reference filter="filter1,filter2,default"/>

  case 3: 其執行順序爲, filter1->原生filter子鏈->filter2, 同時去掉原生的TokenFilter(token)

<dubbo:service filter="filter1,default,filter2,-token"/>

 

總結:   回過頭來看, Dubbo愈來愈像一個精品, 其設計的filter機制(分組/順序控制), 很是的巧妙, 值得拜讀和學習.

相關文章
相關標籤/搜索