【SpringCloud】Gateway Filter Factories && 过滤器执行顺序 && 自定义过滤器

文章目录

  • [一、Gateway Filter Factories(网关过滤器工厂)](#一、Gateway Filter Factories(网关过滤器工厂))
  • 二、过滤器执行顺序
  • 三、自定义过滤器
    • [① 自定义GatewayFilter](#① 自定义GatewayFilter)
      • [1. 创建 GatewayFilterFactory(核心)](#1. 创建 GatewayFilterFactory(核心))
      • [2. 配置过滤器](#2. 配置过滤器)
      • [3. 测试](#3. 测试)
    • [② 自定义GlobalFilter](#② 自定义GlobalFilter)
      • [1. 定义GlobalFilter](#1. 定义GlobalFilter)
      • [2. 测试](#2. 测试)

一、Gateway Filter Factories(网关过滤器工厂)

Predicate 决定了请求由哪一个路由处理。如果在请求处理前后需要加一些逻辑,这就是 Filter(过滤器)的作用范围了。

  • Predicate :决定 "进不进这个路由"
  • Filter :决定 "进来后怎么处理"
Plain 复制代码
Request
   ↓
RoutePredicate(匹配路由)
   ↓
GatewayFilter(改请求 / 鉴权 / 限流)
   ↓
转发到下游服务

Filter 分为两种类型:

  • Pre 类型过滤器:路由处理之前执行(请求转发到后端服务之前执行),在 Pre 类型过滤器中可以做鉴权、限流等。
  • Post 类型过滤器:请求执行完成后,将结果返回给客户端之前执行。

Spring Cloud Gateway 中内置了很多 Filter,用于拦截和链式处理web请求。比如权限校验、访问超时等设定。从作用范围上,Filter 可分为两种:

  • GatewayFilter:应用到单个路由或者一个分组的路由上。
  • GlobalFilter:应用到所有的路由上,即对所有的请求生效。

1. GatewayFilter

GatewayFilter 与 Predicate 类似,都是在配置文件 application.yml 中配置,每个过滤器的逻辑都是固定的。

比如 AddRequestParameterGatewayFilterFactory 只需要在配置文件中写 AddRequestParameter,就可以为所有的请求添加一个参数,我们先通过一个例子来演示 GatewayFilter 如何使用。

快速上手

  1. application.yml 中添加 filter

    yaml 复制代码
       server:
         port: 10020  # 网关端口
       
       spring:
         application:
           name: gateway  # 应用名称
         cloud:
           nacos:
             discovery:
               server-addr: lirendada.art:8848  # Nacos地址
           gateway:
             routes:
               - id: order-service        
                 uri: lb://order-service 
                 predicates:              
                   - Path=/order/**,/feign/**
                 filters:
                   - addRequestParameter=userName, lirendada  # 每个请求都带上键值对{userName, lirendada}
               - id: product-service
                 uri: lb://product-service
                 predicates:
                   - Path=/product/**
                   - After=2026-12-13T14:08:33.643172900+08:00[Asia/Shanghai]

该 filter 只添加在了 order-service 路由下,因此只对 order-service 路由生效。

  1. 接收参数并打印:在 order-service 服务中接收请求的参数,并打印出来:

    java 复制代码
       @Slf4j
       @RestController
       @RequestMapping("/feign")
       public class FeginController {
           @Autowired
           private ProductApi productApi;
       
           @RequestMapping("/t1")
           public String test1(String userName) {
               log.info("接收到filter请求添加的参数:userName={}", userName);
               return productApi.p1(1);
           }
       }
  2. 测试:访问 http://127.0.0.1:8080/feign/t1 以及 http://127.0.0.1:10020/feign/t1 观察区别:

说明

Spring Cloud Gateway 提供了的 Filter 非常多,下面列出一些常见过滤器的说明。

详细可参考官方文档:GatewayFilterFactories

default-filters

前面提到的 filter 都是添加在指定路由下的,所以只对当前路由生效。若需要对全部路由生效,可以使用 spring.cloud.gateway.default-filters,这个属性需要一个 filter 的列表。

举个例子:

yaml 复制代码
spring:
  application:
    name: gateway  # 应用名称
  cloud:
    nacos:
      discovery:
        server-addr: lirendada.art:8848  
    gateway:
      routes:
        - id: order-service        
          uri: lb://order-service  
          predicates:             
            - Path=/order/**,/feign/**
          filters:
            - AddRequestParameter=userName, lirendada
        - id: product-service
          uri: lb://product-service
          predicates:
            - Path=/product/**
            - After=2026-12-13T14:08:33.643172900+08:00[Asia/Shanghai]
      default-filters:  # 和routes是同级的
        - name: Retry
          args:
            retries: 3
            series: BAD_GATEWAY

然后修改一下之前 controller 的代码:

java 复制代码
@RequestMapping("/t1")
public String test1(String userName, HttpServletResponse response) {
    log.info("接收到filter请求添加的参数:userName={}", userName);
    response.setStatus(502);
    return productApi.p1(1);
}

访问 http://127.0.0.1:10020/feign/t1 观察:

2. GlobalFilter

GlobalFilter 是 Spring Cloud Gateway 中的全局过滤器,它和 GatewayFilter 的作用是相同的,但和 default-filters 有以下区别:

维度 default-filters GlobalFilter
配置方式 YAML Java
生效范围 所有路由 所有路由
灵活性 极高
是否写代码
是否可访问业务资源
顺序控制 固定(内部) 可精确控制
可维护性 依赖实现质量

Spring Cloud Gateway 内置的全局过滤器也有很多,比如:

  • The Gateway Metrics Filter:网关指标,提供监控指标
  • Forward Routing Filter:用于本地 forword,请求不转发到下游服务器
  • ReactiveLoadBalancerClientFilter:针对下游服务,实现负载均衡
  • ......

更多过滤器参考:GlobalFilters

快速上手

  1. 添加依赖

    xml 复制代码
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-actuator</artifactId>
       </dependency>
  2. 添加配置

    yaml 复制代码
       spring:
         cloud:
           gateway:
             metrics:
               enabled: true
       management:
         endpoints:
           web:
             exposure:
               include: "*"
         endpoint:
           health:
             show-details: always
           shutdown:
             enabled: true
  3. 测试:访问 http://127.0.0.1:10020/actuator,显示所有监控的信息链接

二、过滤器执行顺序

一个项目中,既有 GatewayFilter,又有 GlobalFilter 时,执行的先后顺序是什么呢?

请求路由后,网关会把当前项目中的 GatewayFilterGlobalFilter 合并到一个过滤器链中,并进行排序,依次执行过滤器。

每一个过滤器都必须指定一个 int 类型的 order 值,默认值为 0,表示该过滤的优先级。order 值越小,优先级越高,执行顺序越靠前

  • Filter 通过实现 Order 接口 或者添加 @Order 注解来指定 order 值。
  • Spring Cloud Gateway 提供的 Filter 由 Spring 指定,当然用户也可以自定义 Filter,由用户指定。
  • 当过滤器的 order 值一样时,会按照 defaultFilter > GatewayFilter > GlobalFilter 的顺序执行。

三、自定义过滤器

Spring Cloud Gateway 提供了过滤器的扩展功能,开发者可以根据实际业务来自定义过滤器,同样自定义过滤器也支持 GatewayFilterGlobalFilter 两种。

① 自定义GatewayFilter

自定义 GatewayFilter,需要去实现接口 GatewayFilterFactory,不过 SpringBoot 默认帮我们实现了一个抽象类 AbstractGatewayFilterFactory,所以直接继承该抽象类即可。

1. 创建 GatewayFilterFactory(核心)

java 复制代码
@Slf4j
@Component
public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomGatewayFilterFactory.CustomConfig> implements Ordered {
    /**
     * 告诉 Gateway:这个 GatewayFilterFactory 使用哪一种配置对象,如何把 YAML 中的参数绑定进来。
     */
    public CustomGatewayFilterFactory() {
        super(CustomConfig.class);
    }

    @Override
    public GatewayFilter apply(CustomConfig config) {
        /**
         * Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain)
         *  ServerWebExchange: HTTP请求-响应交互的契约, 提供对HTTP请求和响应的访问, 服务器端请求属性, 请求实例,响应实例等, 类似Context角色
         *  GatewayFilterChain: 过滤器链
         *  Mono: Reactor核心类, 数据流发布者, Mono最多只触发一个事件, 所以可以把Mono 用于在异步任务完成时发出通知.
         *  Mono.fromRunnable: 创建一个包含Runnable元素的数据流
         */
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                log.info("[Pre] Custom Gateway Filter");  // 自定义的 GatewayFilter 逻辑
                return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                    log.info("[Post] Custom Gateway Filter");
                }));
            }
        };
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE; // 配置优先级, order越大, 优先级越低
    }

    @Data
    public static class CustomConfig {
        private String name;
    }
}
  1. 自定义过滤器的类名统一以 GatewayFilterFactory 结尾。因为默认情况下,过滤器的 name 会采用该定义类的前缀,这里的 name=Custom。
  2. apply() 方法中,可以同时包含 Pre 和 Post 过滤,并且 then() 方法是请求执行结束之后处理的。
  3. CustomConfig 是一个配置类,该类我们设定只有一个属性 name,会和 yml 的配置对应。
  4. 该类需要交给 Spring 管理,所以需要加五大注解。
  5. getOrder 表示该过滤器的优先级,值越大,优先级越低。

2. 配置过滤器

yaml 复制代码
spring:
  application:
    name: gateway  
  cloud:
    nacos:
      discovery:
        server-addr: lirendada.art:8848  
    gateway:
      routes:
        - id: order-service        
          uri: lb://order-service  
          predicates:              
            - Path=/order/**,/feign/**
          filters:
            - AddRequestParameter=userName, lirendada
            - name: Custom
              args:
                name: lirendada  # 这里的 name 是 CustomConfig 的字段,要和上面区分开

3. 测试

重启服务,访问接口,观察日志:http://127.0.0.1:10020/order/1

② 自定义GlobalFilter

自定义 GlobalFilter 比较简单,它不需要额外的配置,因为它全局生效,所以只需要实现 GlobalFilter 接口,自动会过滤所有的 Filter。

1. 定义GlobalFilter

java 复制代码
@Slf4j
@Component
public class CustomGlobalFilterFactory implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("[Pre] Custom GlobalFilter Filter");  // 自定义的 GlobalFilter 逻辑
        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            log.info("[Post] Custom GlobalFilter Filter");
        }));
    }

    @Override
    public int getOrder() {
        return Ordered.LOWEST_PRECEDENCE;
    }
}

2. 测试

重启服务,访问接口,观察日志:http://127.0.0.1:10020/order/1

从日志中可以看出来,当 GatewayFilterGlobalFilter 过滤器的 order 值一样时,会先执行 GatewayFilter

相关推荐
Andy Dennis2 小时前
Java&Go 内存管理
java·jvm·go
xuzhiqiang07249 小时前
Java进阶之路,Java程序员职业发展规划
java·开发语言
时艰.9 小时前
订单系统历史数据归档方案
java
一只叫煤球的猫11 小时前
ThreadForge v1.1.0 发布:让 Java 并发更接近 Go 的开发体验
java·后端·性能优化
014.11 小时前
2025最新jenkins保姆级教程!!!
java·运维·spring boot·spring·jenkins
浣熊88811 小时前
天机学堂虚拟机静态ip无法使用(重启后ip:192.168.150.101无法使用连接Mobaxterm数据库等等,或者无法使用修改之后的Hosts域名去访问nacos,jenkins)
java·微服务·虚拟机·天机学堂·重启之后静态ip用不了
心 -12 小时前
java八股文IOC
java
I_LPL13 小时前
day34 代码随想录算法训练营 动态规划专题2
java·算法·动态规划·hot100·求职面试
亓才孓13 小时前
【MyBatis Exception】Public Key Retrieval is not allowed
java·数据库·spring boot·mybatis