Appearance
Spring Cloud Gateway Retry GatewayFilter Factory
概述
在微服务架构中,网络通信不可避免地会遇到各种瞬时故障,如网络抖动、服务临时不可用、超时等问题。Spring Cloud Gateway 的 Retry GatewayFilter Factory 提供了强大的重试机制,能够自动重试失败的请求,显著提高系统的稳定性和可用性。
Retry Filter 是网关层面的容错机制,它能够在请求失败时自动重试,避免因临时性故障导致的服务不可用。
业务场景
在实际业务场景中,Retry Filter 主要解决以下问题:
- 网络瞬时故障:解决因网络抖动导致的连接失败
- 服务临时过载:当下游服务临时过载返回 5XX 错误时,通过重试等待服务恢复
- 超时处理:处理网络延迟导致的超时问题
- 提高系统可用性:减少因临时性故障导致的业务中断
配置参数详解
Retry GatewayFilter Factory 支持以下配置参数:
| 参数 | 类型 | 描述 | 默认值 |
|---|---|---|---|
retries | int | 重试次数 | 3 次 |
statuses | List | 需要重试的 HTTP 状态码 | 5XX 系列 |
methods | List | 需要重试的 HTTP 方法 | GET |
series | List | 需要重试的状态码系列 | 5XX 系列 |
exceptions | List | 需要重试的异常类型 | IOException, TimeoutException |
backoff | Object | 指数退避配置 | 禁用 |
jitter | Object | 抖动配置 | 禁用 |
timeout | Duration | 重试超时时间 | 无限制 |
退避策略 (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)一起使用,形成更完善的容错机制。