前言:
剛剛寫了篇博文: 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機制(分組/順序控制), 很是的巧妙, 值得拜讀和學習.