Skip to content

Spring Cloud Gateway Retry GatewayFilter Factory

概述

在微服务架构中,网络通信不可避免地会遇到各种瞬时故障,如网络抖动、服务临时不可用、超时等问题。Spring Cloud Gateway 的 Retry GatewayFilter Factory 提供了强大的重试机制,能够自动重试失败的请求,显著提高系统的稳定性和可用性。

Retry Filter 是网关层面的容错机制,它能够在请求失败时自动重试,避免因临时性故障导致的服务不可用。

业务场景

在实际业务场景中,Retry Filter 主要解决以下问题:

  • 网络瞬时故障:解决因网络抖动导致的连接失败
  • 服务临时过载:当下游服务临时过载返回 5XX 错误时,通过重试等待服务恢复
  • 超时处理:处理网络延迟导致的超时问题
  • 提高系统可用性:减少因临时性故障导致的业务中断

配置参数详解

Retry GatewayFilter Factory 支持以下配置参数:

参数类型描述默认值
retriesint重试次数3 次
statusesList需要重试的 HTTP 状态码5XX 系列
methodsList需要重试的 HTTP 方法GET
seriesList需要重试的状态码系列5XX 系列
exceptionsList需要重试的异常类型IOException, TimeoutException
backoffObject指数退避配置禁用
jitterObject抖动配置禁用
timeoutDuration重试超时时间无限制

退避策略 (Backoff)

退避策略用于控制重试间隔时间,避免对已经过载的服务造成更大压力:

  • 公式firstBackoff * (factor ^ n),其中 n 是重试次数
  • 最大退避时间:可通过 maxBackoff 限制
  • 基于前值计算:当 basedOnPreviousValue 为 true 时,使用 prevBackoff * factor 计算

抖动策略 (Jitter)

抖动策略在退避时间基础上增加随机性,避免多个请求同时重试:

  • 计算范围[backoff - backoff*randomFactor, backoff + backoff*randomFactor]

基础配置示例

kotlin
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: http://localhost:8080/users
          predicates:
            - Path=/api/users/**
          filters:
            - name: Retry
              args:
                retries: 3                    # 重试3次
                statuses: BAD_GATEWAY         # 当返回502时重试
                methods: GET,POST             # 只对GET和POST请求重试
                backoff:
                  firstBackoff: 10ms          # 第一次重试等待10ms
                  maxBackoff: 50ms            # 最大等待时间50ms
                  factor: 2                   # 每次重试时间翻倍
                  basedOnPreviousValue: false # 不基于前值计算
                jitter:
                  randomFactor: 0.5           # 50%的随机抖动
                timeout: 100ms                # 每次重试超时时间
java
spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: http://localhost:8080/users
          predicates:
            - Path=/api/users/**
          filters:
            - name: Retry
              args:
                retries: 3                    # 重试3次
                statuses: BAD_GATEWAY         # 当返回502时重试
                methods: GET,POST             # 只对GET和POST请求重试
                backoff:
                  firstBackoff: 10ms          # 第一次重试等待10ms
                  maxBackoff: 50ms            # 最大等待时间50ms
                  factor: 2                   # 每次重试时间翻倍
                  basedOnPreviousValue: false # 不基于前值计算
                jitter:
                  randomFactor: 0.5           # 50%的随机抖动
                timeout: 100ms                # 每次重试超时时间

编程式配置

除了 YAML 配置,还可以通过 Java 代码进行配置:

kotlin
@Configuration
class RetryConfiguration {

    @Bean
    fun customRouteLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("order-service-retry") { r ->
                r.path("/api/orders/**")
                    .filters { f ->
                        f.retry { retryConfig ->
                            retryConfig
                                .retries(3)                           // 重试3次
                                .statuses(HttpStatus.INTERNAL_SERVER_ERROR, HttpStatus.BAD_GATEWAY)
                                .methods(HttpMethod.GET, HttpMethod.POST)
                                .backoff(
                                    Duration.ofMillis(10),            // 首次退避10ms
                                    Duration.ofMillis(100),           // 最大退避100ms
                                    2,                                // 退避因子
                                    false                             // 不基于前值计算
                                )
                        }
                    }
                    .uri("http://localhost:8081")
            }
            .build()
    }
}
java
@Configuration
public class RetryConfiguration {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
            .route("order-service-retry", r ->
                r.path("/api/orders/**")
                    .filters(f ->
                        f.retry(retryConfig ->
                            retryConfig
                                .retries(3)                           // 重试3次
                                .statuses(HttpStatus.INTERNAL_SERVER_ERROR, HttpStatus.BAD_GATEWAY)
                                .methods(HttpMethod.GET, HttpMethod.POST)
                                .backoff(
                                    Duration.ofMillis(10),            // 首次退避10ms
                                    Duration.ofMillis(100),           // 最大退避100ms
                                    2,                                // 退避因子
                                    false                             // 不基于前值计算
                                )
                        )
                    )
                    .uri("http://localhost:8081")
            )
            .build();
    }
}

简化配置语法

Spring Cloud Gateway 还支持简化的配置语法:

yaml
spring:
  cloud:
    gateway:
      routes:
        - id: payment-service
          uri: https://payment.example.com
          filters:
            # 完整配置
            - name: Retry
              args:
                retries: 3
                statuses: INTERNAL_SERVER_ERROR
                methods: GET
                backoff:
                  firstBackoff: 10ms
                  maxBackoff: 50ms
                  factor: 2
                  basedOnPreviousValue: false
                jitter:
                  randomFactor: 0.5
                timeout: 100ms

        - id: payment-service-shortcut
          uri: https://payment.example.com
          filters:
            # 简化配置:重试次数,状态码,方法,首次退避,最大退避,因子,是否基于前值,抖动因子,超时
            - Retry=3,INTERNAL_SERVER_ERROR,GET,10ms,50ms,2,false,0.5,100ms

实际业务场景示例

场景 1:电商订单服务重试

kotlin
// 电商系统中,订单服务可能因为库存检查服务临时不可用而失败
spring:
  cloud:
    gateway:
      routes:
        - id: order-service
          uri: http://order-service:8080
          predicates:
            - Path=/api/orders/**
          filters:
            - name: Retry
              args:
                retries: 5                    # 订单创建比较重要,重试5次
                statuses: INTERNAL_SERVER_ERROR,BAD_GATEWAY,SERVICE_UNAVAILABLE
                methods: POST                 # 只对POST请求重试(创建订单)
                exceptions: java.net.SocketTimeoutException,java.io.IOException
                backoff:
                  firstBackoff: 100ms         # 第一次重试等待100ms
                  maxBackoff: 2000ms          # 最大等待2秒
                  factor: 2                   # 指数增长
                jitter:
                  randomFactor: 0.3           # 30%随机抖动,避免雪崩
                timeout: 5000ms               # 每次重试最多等待5秒

场景 2:支付服务重试

kotlin
// 支付服务对稳定性要求极高,需要更保守的重试策略
spring:
  cloud:
    gateway:
      routes:
        - id: payment-service
          uri: http://payment-service:8080
          predicates:
            - Path=/api/payments/**
          filters:
            - name: Retry
              args:
                retries: 3
                statuses: GATEWAY_TIMEOUT,SERVICE_UNAVAILABLE  # 只重试超时和服务不可用
                methods: GET                  # 只对查询操作重试,避免重复扣款
                backoff:
                  firstBackoff: 200ms         # 支付服务重试间隔稍长
                  maxBackoff: 1000ms
                  factor: 1.5                 # 较温和的增长因子
                timeout: 3000ms

重要注意事项

1. forward: 前缀 URL 的处理

当使用 `forward:` 前缀的 URL 时,目标端点应该谨慎编写,确保在错误情况下不会向客户端发送已提交的响应。

kotlin
// ❌ 错误的做法
@RestController
class OrderController {

    @PostMapping("/orders")
    fun createOrder(@RequestBody order: Order): ResponseEntity<String> {
        return try {
            orderService.create(order)
            ResponseEntity.ok("订单创建成功")
        } catch (e: Exception) {
            // 这样会直接返回错误响应,重试机制不会生效
            ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body("订单创建失败")
        }
    }
}

// ✅ 正确的做法
@RestController
class OrderController {

    @PostMapping("/orders")
    fun createOrder(@RequestBody order: Order): Mono<String> {
        return orderService.create(order)
            .map { "订单创建成功" }
            .onErrorMap { exception ->
                // 抛出异常,让重试机制处理
                RuntimeException("订单创建失败: ${exception.message}", exception)
            }
    }
}

2. 过滤器执行顺序

重试过滤器会重新执行它后面的所有过滤器,确保后续过滤器能够正确处理多次执行。

kotlin
spring:
  cloud:
    gateway:
      routes:
        - id: example-route
          uri: http://backend-service
          filters:
            - AddRequestHeader=X-Request-Start, ${now}  # 这个会执行多次
            - name: Retry
              args:
                retries: 3
            - AddRequestHeader=X-Retry-Attempt, ${attempt}  # 这个也会执行多次

3. 请求体缓存问题

对于包含请求体的 HTTP 方法(如 POST、PUT),请求体会被缓存在内存中,可能导致网关内存受限。

kotlin
// 请求体会被缓存在 ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR 中
// 类型为 org.springframework.core.io.buffer.DataBuffer

@Component
class RequestBodyCacheMonitor : GlobalFilter {

    override fun filter(exchange: ServerWebExchange, chain: GatewayFilterChain): Mono<Void> {
        val cachedBody = exchange.getAttribute<DataBuffer>(
            ServerWebExchangeUtils.CACHED_REQUEST_BODY_ATTR
        )

        if (cachedBody != null) {
            // 监控缓存的请求体大小
            val bodySize = cachedBody.readableByteCount()
            logger.info("缓存的请求体大小: $bodySize 字节")

            // 对于大请求体,可以考虑限制重试
            if (bodySize > 1024 * 1024) { // 1MB
                logger.warn("请求体过大($bodySize 字节),建议谨慎使用重试")
            }
        }

        return chain.filter(exchange)
    }
}

重试机制的工作流程

最佳实践

1. 合理设置重试次数

kotlin
# 根据服务类型设置不同的重试策略
spring:
  cloud:
    gateway:
      routes:
        # 查询服务:可以多重试
        - id: query-service
          filters:
            - name: Retry
              args:
                retries: 5
                methods: GET

        # 写操作服务:谨慎重试
        - id: write-service
          filters:
            - name: Retry
              args:
                retries: 2
                methods: GET  # 只对查询操作重试

2. 配置合适的退避策略

kotlin
# 避免惊群效应的退避配置
backoff:
  firstBackoff: 100ms      # 不要太短,给下游服务恢复时间
  maxBackoff: 5000ms       # 设置上限,避免无限等待
  factor: 1.5              # 温和的增长因子
  basedOnPreviousValue: false

jitter:
  randomFactor: 0.3        # 30%的随机性,避免同时重试

3. 监控重试情况

kotlin
@Component
class RetryMetricsCollector : GlobalFilter {

    private val retryCounter = Counter.builder("gateway.retry.attempts")
        .description("网关重试次数统计")
        .register(Metrics.globalRegistry)

    override fun filter(exchange: ServerWebExchange, chain: GatewayFilterChain): Mono<Void> {
        val retryAttempt = exchange.getAttribute<Int>("retry.attempt") ?: 0

        return chain.filter(exchange)
            .doOnError {
                if (retryAttempt > 0) {
                    retryCounter.increment(
                        Tags.of(
                            "route", exchange.getAttribute<String>("routeId") ?: "unknown",
                            "attempt", retryAttempt.toString()
                        )
                    )
                }
            }
    }
}

总结

Spring Cloud Gateway 的 Retry Filter 是构建高可用微服务架构的重要组件。通过合理配置重试策略,可以有效应对网络抖动、服务临时故障等问题,显著提升系统的稳定性。

关键要点:

  • 🔄 智能重试:支持多种重试条件和策略
  • ⏱️ 退避机制:避免对故障服务造成额外压力
  • 🎯 精确控制:可针对特定状态码、方法、异常进行重试
  • 📊 可观测性:需要配合监控了解重试效果
  • ⚠️ 注意事项:合理使用,避免级联故障

在生产环境中,建议结合熔断器(Circuit Breaker)一起使用,形成更完善的容错机制。