理解Spring Cloud Gateway Filters的執行順序

本文基於Spring Cloud Gateway 2.1.1.RELEASE。html

在講SCG的Filter的排序問題以前得先比較一下Spring Cloud Gateway在對待Filter的方面與Zuul2有着哪些不一樣。java

Filter的Scope

  • SCG採用的是Global Filter和Route Filter相結合的方式
  • Zuul2則都是Global Filter

SCG所謂Route Filter就是像下面這樣的:git

spring:
  cloud:
    gateway:
      routes:
        - id: tomcat_route
          uri: http://tomcat:8080
          predicates:
            - Path=/tomcat/docs
          filters:
            - StripPrefix=1
            - RemoveRequestHeader=X-Request-Foo

上面的StripPrefixRemoveRequestHeader就是Route Filter,而SCG的Global Filter則是隱式的,無需顯式配置,它們會在請求過來的時候被SCG調用。github

也就是說你能夠配置不一樣的Route,而後爲每一個Route配置不一樣的Route Filter,這一切都是在配置階段就決定下來的。spring

而Zuul2則都是Global Filter,所以你得運行時在每一個Filter內部本身決定是否要幹活,除此以外,發送到Origin(被代理的服務)的url也得你本身設置,下面是一個例子(來自Zuul2 Sample):api

public class Routes extends HttpInboundSyncFilter {
  @Override
  public boolean shouldFilter(HttpRequestMessage httpRequestMessage) {
    // ...
    return true;
  }
  @Override
  public HttpRequestMessage apply(HttpRequestMessage request) {
    // ...
    // Route healthchecks to the healthcheck endpoint.;
    context.setEndpoint(ZuulEndPointRunner.PROXY_ENDPOINT_FILTER_NAME);
    context.setRouteVIP("tomcat");

    return request;
  }
}

Filter的角色

  • 在SCG概念中只有一種Filter(撇開Global和Route的區別),它用代碼來區分Pre Filter、Post Filter。在文檔中還提到了Routing Filter,其實也是Pre Filter。
  • Zuul2在代碼中顯示得提供了InboundFilter(負責進來的請求)、OutboundFilter(負責出去的響應)、ProxyEndpoint(負責請求到Origin,串起Inbound和Outbound)。

下面是SCG的Pre Filter(裁剪自官方例子12.2 Writing Custom GatewayFilter Factories):tomcat

public class PreGatewayFilterFactory extends AbstractGatewayFilterFactory {
    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            // business logic
            return chain.filter();
        };
    }
}

Post Filter的例子:app

public class PostGatewayFilterFactory extends AbstractGatewayFilterFactory {
    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            return chain.filter(exchange).then(/* business logic */);
        };
    }
}

在Zuul2裏,你則得分別實現HttpInboundSyncFilterHttpOutboundSyncFilterProxyEndpoint不須要你本身實現。ide

SCG Filter的問題

SCG的優勢很明顯,它作了Zuul2不作的事情:post

  1. 替你決定進來的請求轉發到哪一個Origin。在Zuul2裏這個交給你本身來實現。
  2. 在配置上就決定了這個Route會應用哪些Filter。在Zuul2裏這個交給你本身來判斷。

可是隨着對SCG的深刻了解,發現了關於Filter的執行順序存在一些坑,若是不瞭解清楚會容易出錯。

Filter的排序

前面講了,SCG在執行過程當中Global Filter和Route Filter是一塊兒執行的,那麼它們的order是怎樣的?

先來看看Global Filter,你能夠訪問/actuator/gateway/globalfilters(見文檔)獲得Global Filter的排序:

clipboard.png

那麼若是你寫了一個自定義 Global Filter,那麼它的order是什麼呢?這個要看狀況:

  • 若是你的自定義Global Filter實現了Ordered接口或者寫了@Order註解,那麼它的order就是它本身設定的值
  • 不然,它就沒有order

關於這點能夠看FilteringWebHandler.java的源代碼

再來看看Route Filter,這也分兩種狀況:

  • 若是RouteFilter實現了Ordered接口或者寫了@Order註解,那麼它的order就是它本身設定的值。
  • 不然,它的order則是從1開始,按照Route中定義的順序依次排序。

關於這點能夠看RouteDefinitionRouteLocator.java的源代碼

最後SCG把它們兩個結合起來,作一個排序,對於沒有order的Filter,它的order則默認爲Ordered.LOWEST_PRECEDENCE。關於這點能夠看FilteringWebHandler.java的源代碼

用一張圖作總結:

clipboard.png

Filter的執行順序

先看SCG文檔3. How It Works中的這張圖:

這張圖大概告訴你了SCG的調用過程,能夠看到通過了一堆Filters,可是並無告訴你Filter的執行順序。而後在SCG的6.1 Combined Global Filter and GatewayFilter Ordering提到了:

As Spring Cloud Gateway distinguishes between "pre" and "post" phases for filter logic execution (see: How It Works), the filter with the highest precedence will be the first in the "pre"-phase and the last in the "post"-phase.

也就是說意思若是這個Filter是Pre Filter,那麼執行順序和排序順序相同,若是這個Filter是Post Filter則執行順序和排序順序相反。我整理了一下SCG自帶GlobalFilter的執行順序:

clipboard.png

能夠看到GatewayMetricsFilter既是Pre Filter也是Post Filter。

總結

  • 執行某個Route的時候,SCG會將Global Filter和Route Filter結合起來並排序:

    • 沒有給order的Global Filter則保持order爲null去排序
    • 沒有給order的Route Filter的order則從1開始,根據Route中定義的順序給值
    • 排序邏輯見AnnotationAwareOrderComparator
  • 對於Pre Filter,執行順序同排序順序
  • 對於Post Filter,執行順序與排序順序相反
  • 若是你要自定義Global Filter,那麼通常來講:

    • 自定義的Global Pre Filter要在Routing Filter以前執行
    • 自定義的Global Post Filter要在Routing Filter以後執行或者NettyWriteResponseFilter以後執行
  • 若是你要自定義Route Filter,那麼通常來講:

    • 自定義Route Pre Filter要在ForwardPathFilterRouteToRequestUrlFilter之間,並且不須要實現Ordered接口或添加@Order註解
    • 自定義的Route Post Filter比較少見,放在Routing Filter或者NettyWriteResponseFilter以後執行
相關文章
相關標籤/搜索