首頁>技術>

引言

最近有個網友問了一個問題,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方法,該方法前面已經介紹過了。

17
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • 超全微服務前後端企業應用系統專案搭建流程,原始碼開源,基於MyBatis、SpringBoot