Spring Cloud Gateway Global Filter vs Gateway filter

Last Modified: 2023/02/06

概述

Spring Cloud Gateway 中的 filter 分为 global filter 和 gateway filter,global filter 作用于所有的路由,gateway filter 一般作用于特定的路由,也可以配置默认 gateway filter,默认 gateway filter 作用于所有路由。不论是 global filter 还是 gateway filter,他们的作用都是类似的:对请求和响应做出某种修改。

注①:以下会交替使用“全局 filter” 和 global filter,他们表达相同的含义。
注②:filter 的逻辑分为两个部分,一个部分逻辑在请求处理之前(pre logic),另一部分处理逻辑在响应之后(post logic),所以如果 A filter 优先级高于 B filter,那么 A 的 pre logic 在 B 的 pre logic 之前执行,但是 B 的 post logic 在 A 的 post logic 之前执行。
注③:严格来说,pre logic 执行是在上一个 filter 的 pre logic 之后执行,post logic 在上一个 filter 的 post logic 之前或响应之后执行。
注④:pre logic 和 post logic 更多说明参考最后一节。

Global Filter

Global Filter 的接口定义如下:

public interface GlobalFilter {
  /**
   * Process the Web request and (optionally) delegate to the next {@code WebFilter}
   * through the given {@link GatewayFilterChain}.
   * @param exchange the current server exchange
   * @param chain provides a way to delegate to the next filter
   * @return {@code Mono<Void>} to indicate when request processing is complete
   */
  Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

全局 filter 一个典型的用法是身份认证拦截器,只需要实现 GlobalFilter 接口即可,如果有多个全局 filter,且拦截器逻辑之间存在顺序关系,则需要实现 Ordered 接口。

@Component
public class AuthFilter implements GlobalFilter, Ordered {
  @Override
  public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    // 这里实现身份认证逻辑,
  }
  // ...
}

Gateway Filter

作用于特定的路由,他的作用和 Global filter 相同,都是为了修改请求或者响应,比方说给请求加上某个请求头,或者给响应加上某个响应头等等。一个路由可以配置多个 filter,给路由配置 filter 很简单,例如我们想给 id 为 some_route 的路由配置一个 AddRequestHeader filter,只需要配置如下:

spring:
  cloud:
    gateway:
      routes:
      - id: some_route
        uri: https://example.org
        filters:
        - AddRequestHeader=X-Request-red, blue

spring 内置了很多常用的 filter,大家可自行查看。上面的配置中 filters 是一个数组,目前只配置了 AddRequestHeader filter,filter 配置的语法是:<filter name>=<filter config params>

filter 的名字是 filter 对应的工厂类的名字去掉 “GatewayFilterFactory” 而得来,AddRequestHeader filter 对应的工厂类为 AddRequestHeaderGatewayFilterFactory,因此 filter 的名字为 AddRequestHeader。filter config params 则是传递给 filter 的配置参数,配置参数也是在 filter 工厂类中通过内部类的方式定义的。

举个例子,我们实现一个黑名单拦截的过滤器,可以自定义一个以 GatewayFilterFactory 作为结尾的类 BlackListIpGatewayFilterFactory(注:非必须,但是是推荐的命名方式),实现 GatewayFilterFactory 接口或者继承 AbstractGatewayFilterFactory 抽象类,同时定义一个内部类 Config,用于接收配置参数,代码如下:

@Component
public class BlackListIpGatewayFilterFactory extends AbstractGatewayFilterFactory<BlackListIpGatewayFilterFactory.Config> {
  @Override
  public GatewayFilter apply(Config config) {
      return (exchange, chain) -> {
          // 如果请求 ip 在 blacklistIps 中就拦截,这里省略实现部分
          return chain.filter(exchange);
      };
  }
  
  public BlackListIpFilter() {
      super(Config.class);
  }
  
  public static class Config {
      private List<String> blacklistIps;
      public List<String> getBlacklistIps() {
          return blacklistIps;
      }
      public void setBlacklistIps(List<String> blacklistIps) {
          this.blacklistIps = blacklistIps;
      }
  }
}

从上面的代码可以看出,过滤器的逻辑实现被封装在 apply 方法返回的 GatewayFilter 对象中,实现了 filter 之后,我们就可以将 filter 配置到特定的路由上:

spring:
  cloud:
    gateway:
      routes:
      - id: some_route1
        uri: https://example.org
        filters:
        - BlackListIp=192.168.2.124,192.168.2.158

默认 Gateway Filter

在概述部分提到了默认 gateway filter 也可应用于所有路由,那么“默认 gateway filter” 和全局 filter 有什么区别呢?在讨论这个问题之前,先看默认 Gateway filter 的配置方式:

spring:
  cloud:
    gateway:
      default-filters:
      - AddResponseHeader=X-Response-Default-Red, Default-Blue

默认 gateway filter 和全局 filter 在于:每个路由都会有一个默认 gateway filter 实例,但是全局 filter 是所有路由共享的。

框架层面无区别

不论是全局 filter 还是 gateway filter,他们最终都是被包装成 OrderedGatewayFilter,然后排序这些 filter,让请求和响应依次经过排序后的 filter 处理。所以在框架层面他们都是 filter,目的都是为了处理请求和响应,并无本质的不同。所以官方给的架构图并没有区分 global filter 和 gateway filter:

一个路由的 filters 包括 global filters 和该路由本身配置的 gateway filters(route scoped),这一点可以在 FilteringWebHandler#handle 方法中看出:

@Override
public Mono<Void> handle(ServerWebExchange exchange) {
  Route route = exchange.getRequiredAttribute(GATEWAY_ROUTE_ATTR);
  // 路由本身的 filters
  List<GatewayFilter> gatewayFilters = route.getFilters();
  // 全局 filters
  List<GatewayFilter> combined = new ArrayList<>(this.globalFilters);
  // 全局 filters + 路由本身的 filters
  combined.addAll(gatewayFilters);
  
  AnnotationAwareOrderComparator.sort(combined);
  if (logger.isDebugEnabled()) {
      logger.debug("Sorted gatewayFilterFactories: " + combined);
  }
  return new DefaultGatewayFilterChain(combined).filter(exchange);
}

pre logic and post logic

@Override
public GatewayFilter apply(Config config) {
    return (exchange, chain) -> {
        // pre loigic 写在 chain.filter 之前
        // chain.filter 会将请求转发给下一个 filter 处理
        return chain.filter(exchange)
          .then(Mono.fromRunnable(() -> {
              // 写在这个地方的是 post logic
          }));
    };
}
有问题吗?点此反馈!

温馨提示:反馈需要登录