理解Spring Cloud Gateway Filters的执行顺序

6次阅读

共计 3518 个字符,预计需要花费 9 分钟才能阅读完成。

本文基于 Spring Cloud Gateway 2.1.1.RELEASE。

在讲 SCG 的 Filter 的排序问题之前得先比较一下 Spring Cloud Gateway 在对待 Filter 的方面与 Zuul2 有着哪些不同。

Filter 的 Scope

  • SCG 采用的是 Global Filter 和 Route Filter 相结合的方式
  • Zuul2 则都是 Global Filter

SCG 所谓 Route Filter 就是像下面这样的:

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 调用。

也就是说你可以配置不同的 Route,然后为每个 Route 配置不同的 Route Filter,这一切都是在配置阶段就决定下来的。

而 Zuul2 则都是 Global Filter,因此你得运行时在每个 Filter 内部自己决定是否要干活,除此之外,发送到 Origin(被代理的服务)的 url 也得你自己设置,下面是一个例子(来自 Zuul2 Sample):

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):

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

Post Filter 的例子:

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

在 Zuul2 里,你则得分别实现 HttpInboundSyncFilterHttpOutboundSyncFilterProxyEndpoint不需要你自己实现。

SCG Filter 的问题

SCG 的优点很明显,它做了 Zuul2 不做的事情:

  1. 替你决定进来的请求转发到哪个 Origin。在 Zuul2 里这个交给你自己来实现。
  2. 在配置上就决定了这个 Route 会应用哪些 Filter。在 Zuul2 里这个交给你自己来判断。

但是随着对 SCG 的深入了解,发现了关于 Filter 的执行顺序存在一些坑,如果不了解清楚会容易出错。

Filter 的排序

前面讲了,SCG 在执行过程中 Global Filter 和 Route Filter 是一起执行的,那么它们的 order 是怎样的?

先来看看 Global Filter,你可以访问/actuator/gateway/globalfilters(见文档)得到 Global Filter 的排序:

那么如果你写了一个自定义 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 的源代码。

用一张图做总结:

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 的执行顺序:

可以看到 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 之后执行
正文完
 0