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
方式二:全局过滤器结合路由元数据实现自动控制
通过全局过滤器根据路由元数据判断是否启用重试,无需手动给路由添加过滤器:
- 定义默认重试过滤器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); }
- 自定义全局过滤器,根据元数据控制重试:
@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; } }
- 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
相关产品推荐
相关产品推荐

