You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Cloud Gateway(v3.1.3)如何配置全局默认重试过滤器并按需禁用

问题:Spring Cloud Gateway 全局重试过滤器配置与特定路由禁用方案

需求背景

在Spring Cloud Gateway(v3.1.3)中,需要为所有路由配置默认重试过滤器,同时允许特定路由禁用该重试逻辑。

尝试的配置(无效)

最初尝试通过YAML配置实现,但受限于RetryGatewayFilterFactory的校验逻辑无法生效:

spring:
  cloud:
    gateway:
      default-filters:
      - name: Retry
        args:
          retries: 3
          statuses: BAD_GATEWAY
          methods: GET,POST,PUT,DELETE
          backoff:
            firstBackoff: 10ms
            maxBackoff: 50ms
            factor: 2
            basedOnPreviousValue: false
      routes:
      - id: retry_disabled
        uri: http://localhost:8080/retry_disabled
        filters:
        - name: Retry
          args:
            retries: 0
      - id: retry_enabled
        uri: http://localhost:8080/retry_enabled

原因是RetryConfig.validate()方法强制校验重试次数必须大于0,且series、status、exceptions不能全为空,methods也不能为空,配置retries:0会直接抛出异常:

public void validate() {
    Assert.isTrue(this.retries > 0, "retries must be greater than 0");
    Assert.isTrue(!this.series.isEmpty() || !this.statuses.isEmpty() || !this.exceptions.isEmpty(),
            "series, status and exceptions may not all be empty");
    Assert.notEmpty(this.methods, "methods may not be empty");
    if (this.backoff != null) {
        this.backoff.validate();
    }
}

备选代码方案及疑问

转而考虑通过Java代码配置路由,定义全局重试过滤器Bean,仅在需要启用重试的路由上手动应用:

@Bean
public Function<GatewayFilterSpec, UriSpec> defaultRetryGatewayFilter() {
    return gatewayFilterSpec -> gatewayFilterSpec
        .retry(retryConfig -> {
            RetryGatewayFilterFactory.BackoffConfig backoffConfig = new RetryGatewayFilterFactory.BackoffConfig();

            backoffConfig.setFirstBackoff(Duration.ofMillis(10));
            backoffConfig.setMaxBackoff(Duration.ofMillis(50));
            backoffConfig.setFactor(2);
            backoffConfig.setBasedOnPreviousValue(false);
            retryConfig
                .setRetries(3)
                .allMethods()
                .setSeries(HttpStatus.Series.SERVER_ERROR)
                .setStatuses(HttpStatus.BAD_GATEWAY)
                .setBackoff(backoffConfig);
        });
}

@Bean
public RouteLocator routes(RouteLocatorBuilder builder, Function<GatewayFilterSpec, UriSpec> defaultRetryGatewayFilter) {
    return builder.routes()
        .route("retry_enabled", r -> r
            .path("/retry_enabled")
            .filters(defaultRetryGatewayFilter)
            .uri("lb://foo"))
        .route("retry_disabled", r -> r
            .path("/retry_disabled")
            // 不应用重试过滤器
            .uri("lb://foo"))
        .build();
}

疑问:该单例defaultRetryGatewayFilter是否线程安全?


解答

1. defaultRetryGatewayFilter的线程安全性

该单例Bean是线程安全的,理由如下:

  • 内部仅在调用时创建BackoffConfig实例并配置retryConfig,每个路由的过滤器配置都是独立上下文执行,不存在跨线程状态共享。
  • RetryGatewayFilterFactory的配置类(RetryConfig、BackoffConfig)都是每次调用retry()方法时重新初始化的,不会复用实例。
  • Spring容器管理的单例Bean本身无线程安全风险,该Function仅作为配置模板,实际执行时每个路由都会生成独立的过滤器实例。

2. 更优的全局默认重试+特定路由禁用方案

上述代码方案可行,但路由数量较多时手动添加过滤器会繁琐,推荐以下两种优化方式:

方式一:自定义重试过滤器工厂,支持禁用逻辑

继承RetryGatewayFilterFactory,重写校验和应用逻辑,允许retries=0时跳过重试:

public class CustomRetryGatewayFilterFactory extends RetryGatewayFilterFactory {

    public CustomRetryGatewayFilterFactory() {
        super();
    }

    @Override
    public void validate(RetryConfig config) {
        // 允许retries=0,直接跳过校验
        if (config.getRetries() == 0) {
            return;
        }
        // 其他情况沿用原校验逻辑
        super.validate(config);
    }

    @Override
    public GatewayFilter apply(RetryConfig config) {
        if (config.getRetries() == 0) {
            // 返回空过滤器,不执行重试逻辑
            return (exchange, chain) -> chain.filter(exchange);
        }
        return super.apply(config);
    }
}

之后在YAML中使用自定义过滤器即可:

spring:
  cloud:
    gateway:
      default-filters:
      - name: CustomRetry
        args:
          retries: 3
          statuses: BAD_GATEWAY
          methods: GET,POST,PUT,DELETE
          backoff:
            firstBackoff: 10ms
            maxBackoff: 50ms
            factor: 2
            basedOnPreviousValue: false
      routes:
      - id: retry_disabled
        uri: http://localhost:8080/retry_disabled
        filters:
        - name: CustomRetry
          args:
            retries: 0
      - id: retry_enabled
        uri: http://localhost:8080/retry_enabled

方式二:全局过滤器结合路由元数据实现自动控制

通过全局过滤器根据路由元数据判断是否启用重试,无需手动给路由添加过滤器:

  1. 定义默认重试过滤器Bean:
@Bean
public GatewayFilter defaultRetryFilter() {
    RetryGatewayFilterFactory.BackoffConfig backoffConfig = new RetryGatewayFilterFactory.BackoffConfig();
    backoffConfig.setFirstBackoff(Duration.ofMillis(10));
    backoffConfig.setMaxBackoff(Duration.ofMillis(50));
    backoffConfig.setFactor(2);
    backoffConfig.setBasedOnPreviousValue(false);

    RetryGatewayFilterFactory.RetryConfig retryConfig = new RetryGatewayFilterFactory.RetryConfig();
    retryConfig.setRetries(3);
    retryConfig.allMethods();
    retryConfig.setSeries(HttpStatus.Series.SERVER_ERROR);
    retryConfig.setStatuses(HttpStatus.BAD_GATEWAY);
    retryConfig.setBackoff(backoffConfig);

    return new RetryGatewayFilterFactory().apply(retryConfig);
}
  1. 自定义全局过滤器,根据元数据控制重试:
@Component
public class RetryFilterGlobalConfig implements GlobalFilter, Ordered {

    private final GatewayFilter defaultRetryFilter;

    public RetryFilterGlobalConfig(GatewayFilter defaultRetryFilter) {
        this.defaultRetryFilter = defaultRetryFilter;
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        Route route = exchange.getAttribute(GATEWAY_ROUTE_ATTR);
        if (route != null) {
            // 读取路由元数据中的禁用标记
            Boolean disableRetry = (Boolean) route.getMetadata().get("disableRetry");
            if (Boolean.FALSE.equals(disableRetry)) {
                return defaultRetryFilter.filter(exchange, chain);
            }
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        // 确保在其他路由过滤器前执行
        return Ordered.HIGHEST_PRECEDENCE + 100;
    }
}
  1. YAML路由配置中通过元数据控制:
spring:
  cloud:
    gateway:
      routes:
      - id: retry_disabled
        uri: http://localhost:8080/retry_disabled
        metadata:
          disableRetry: true
      - id: retry_enabled
        uri: http://localhost:8080/retry_enabled

内容的提问来源于stack exchange,提问作者HeatZync

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 01:55:19