引言
最近有個網友問了一個問題,zuul中如果兩個filter的order一樣,是如何排序的?引起了我的興趣,特地去閱讀了它的原始碼。
zuul是幹什麼的如果你有使用過springcloud應該聽說過zuul,它的定位是分散式微服務中的API閘道器服務,當然後面可能要被gateway替代了。zuul是一個L7應用程式閘道器,提供了動態路由,監視,彈性,安全性等功能。zuul的大部分功能是透過filter實現的。
zuul定義了四種不同生命週期的filter
為了方便操作,zuul內建了一些filter,這些filter主要透過@EnableZuulServer和@EnableZuulProxy註解開啟相關功能。@EnableZuulServer註解開啟的filter功能如下:
@EnableZuulProxy註解除了開啟上面這些filter功能之外,還開啟瞭如下的功能:
如何自定義filter只需繼承ZuulFilter類,實現它的filterType、filterOrder、shouldFilter 和 run方法即可,具體實現可參考如下程式碼:
public class LogFilter extends ZuulFilter { @Override public String filterType() { return FilterConstants.PRE_TYPE; } @Override public int filterOrder() { return 1; } @Override public boolean shouldFilter() { return RequestContext.getCurrentContext().sendZuulResponse(); } @Override public Object run() throws ZuulException { RequestContext currentContext = RequestContext.getCurrentContext(); HttpServletRequest request = currentContext.getRequest(); log.info("zuul pre filter-->" + request.getRequestURL() + "-->" + request.getMethod()); return null; }}
上面的四個方法有哪些作用呢?
需要注意的是,要想使zuul的功能生效,切記要在springboot啟動類上定義@EnableZuulServer或@EnableZuulProxy註解,表示開啟zuul的功能。
filterOrder是如何排序的先看看所有的zuulFilter在哪裡執行的,謎底就在FilterProcessor類的runFilters方法中。
該方法很簡單,先獲取所有zuulFilter,然後遍歷所有zuulFilter,呼叫processZuulFilter方法執行具體的zuulFilter,然後將執行結果返回。
我們重點看看這個方法
FilterLoader.getInstance().getFiltersByType(sType);
該方法的具體邏輯
根據filterType從快取中獲取filter集合,如果快取中有直接返回如果快取中沒有,則建立filter集合,將所有filter中跟filterType的filter新增到filter集合中。排序filter集合將新建立的filter集合放入快取。從上面可以看出filter的排序是透過如下方法執行的:
Collections.sort(list);
該方法底層其實是透過list的sort方法實現的
看看ArrayList的sort方法,傳入的Comparator為null
它的底層又是透過Arrays類的靜態方法sort實現的
由於上一步Comparator為null,則會執行sort方法。
該方法是透過ComparableTimSort類的sort方法實現的,這個方法是最核心的方法了
我們可以看到該方法其實是透過binarySort二分查詢排序的。
透過compareTo方法比較大小。
我們回頭再看看ZuulFilter類
它實現了Comparable介面,重寫了compareTo方法
所以,看到這裡我們可以得出結論:ZuulFilter是透過Integer的compare方法比較filterOrder引數值大小來排序的。
如果filterOrder一樣如何排序?我們看看Integer的compare方法具體的邏輯
如果x==y,則返回0,x<y,則返回 -1,否則返回1 前面在二分查詢中,只有x<y時,才會交換位置。
看到這裡,我們得出這樣的結論,如果filterOrder一樣,則Collections.sort(list);排序時不交換位置,這按照ZuulFilter預設載入順序。那麼,ZuulFilter的預設載入順序是怎麼樣的?
它是透過getAllFilters方法獲取ZuulFilter集合,該方法其實返回的是名稱為filters的ConcurrentHashMap的values,即返回Set集合,是無序的。
重要的事情說三遍:如果filterOrder一樣,ZuulFilter是無序的。 重要的事情說三遍:如果filterOrder一樣,ZuulFilter是無序的。 重要的事情說三遍:如果filterOrder一樣,ZuulFilter是無序的。 所以,filterOrder切記不要定義相同的,不然可能會出現無法預知的執行結果。
兩種排序方法自定義排序其實有兩種方法:
實現Comparable介面,重寫compareTo方法,實現Comparator介面,重寫compare方法如果要使用Collections.sort(list);排序,它預設用的是第一種方法,上面的filterOrder之所以可以排序,是因為Integer實現了Comparable介面,重寫了compareTo方法
如果想自己定義排序規則可以透過實現Comparator介面,重寫compare方法。
Collections.sort(list,new Comparator<Integer>(){ @Override public int compare(Integer o1, Integer o2) { return o2 - o1; }});
它的底層也是透過二分查詢實現的
那麼這兩種方法有什麼區別呢?
Comparable介面位於java.lang包下,而Comparator介面位於java.util包下。Comparable介面是內部比較器,一個類如果想要使用Collections.sort(list) 方法進行排序,則需要實現該介面Comparator介面是外部比較器用於對那些沒有實現Comparable介面或者對已經實現的- Comparable中的排序規則不滿意進行排序.無需改變類的結構,更加靈活。彩蛋zuul中是透過filterOrder引數的大小排序的,而在spring中是透過@Order註解排序的。
預設情況下,如果不指定value值,則value是Integer的最大值。由於排序規則是value越小,則排在越靠前,所以如果不指定value值,則它排在最後。
spring是透過OrderComparator類排序的,它實現了Comparator介面,它的doCompare方法實現的排序。
最終也是呼叫Integer類的compare方法,該方法前面已經介紹過了。