引言
最近有個網友問了一個問題,zuul中若是兩個filter的order同樣,是如何排序的?引發了個人興趣,特意去閱讀了它的源碼。java
zuul是幹什麼的
若是你有使用過springcloud應該據說過zuul,它的定位是分佈式微服務中的API網關服務,固然後面可能要被gateway替代了。zuul是一個L7應用程序網關,提供了動態路由,監視,彈性,安全性等功能。zuul的大部分功能是經過filter實現的。web
zuul定義了四種不一樣生命週期的filter面試

爲了方便操做,zuul內置了一些filter,這些filter主要經過@EnableZuulServer
和@EnableZuulProxy
註解開啓相關功能。@EnableZuulServer
註解開啓的filter功能以下:spring

@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;
}
}
上面的四個方法有哪些做用呢?springboot
方法名稱 | 做用 |
---|---|
filterType | filter類型,包含:pre、routing、post和error四種類型 |
filterOrder | 排序,該值越小,filter越早執行 |
shouldFilter | 開關,表示是否須要執行該filter |
run | filter具體的功能方法 |
須要注意的是,要想使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
方法,該方法前面已經介紹過了。
若是這篇文章對您有所幫助,或者有所啓發的話,幫忙掃描下發二維碼關注一下,或者點贊、轉發、在看。在公衆號中回覆:面試、代碼神器、開發手冊、時間管理有超讚的粉絲福利,另外回覆:加羣,能夠跟不少大廠的前輩交流和學習。

本文分享自微信公衆號 - 蘇三說技術(gh_9f551dfec941)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。