韧性与容错:超时、重试、舱壁与熔断
约 1179 字大约 4 分钟
布欧-Lewyon
2026-05-16
首页 › Spring Boot › 架构师进阶 › 韧性与容错:超时、重试、舱壁与熔断
本篇服务 L4 架构师。假设已了解 Spring Boot 基础集成。
分布式系统的韧性(Resilience)不是靠"不发生故障",而是靠故障发生时系统能优雅降级,恢复后能平滑回归。Spring Boot 生态中没有内置"全栈韧性框架"——Resilience4j 是最主流的轻量库,需自行集成。但比工具更重要的是策略选择。
韧性四大模式
超时(Timeout) → 防止等待无限期
重试(Retry) → 容忍瞬时故障
舱壁(Bulkhead) → 限制单个依赖占用过多资源
熔断(Circuit Breaker)→ 快速失败,避免级联Resilience4j 集成
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-spring-boot3</artifactId>
<version>2.3.0</version>
</dependency>resilience4j:
circuitbreaker:
configs:
default:
sliding-window-size: 10
minimum-number-of-calls: 5
failure-rate-threshold: 50
wait-duration-in-open-state: 30s
permitted-number-of-calls-in-half-open-state: 3
retry:
configs:
default:
max-attempts: 3
wait-duration: 500ms
retry-exceptions:
- org.springframework.dao.DataAccessException
timelimiter:
configs:
default:
timeout-duration: 3s
bulkhead:
configs:
default:
max-concurrent-calls: 10
max-wait-duration: 500ms四种模式可自由组合:
@RestController
public class UserController {
// 超时 + 重试 + 熔断 三合一
@GetMapping("/users/{id}")
@TimeLimiter(name = "default")
@Retry(name = "default")
@CircuitBreaker(name = "default", fallbackMethod = "fallbackUser")
public CompletableFuture<User> getUser(@PathVariable Long id) {
return CompletableFuture.supplyAsync(() -> userService.findById(id));
}
// 熔断后的降级方法
public CompletableFuture<User> fallbackUser(Long id, Throwable t) {
log.warn("熔断降级: id={}, cause={}", id, t.getMessage());
return CompletableFuture.completedFuture(User.unknown(id));
}
}架构视角:韧性策略选型
| 模式 | 应对的问题 | 风险 |
|---|---|---|
| 超时(Timeout) | 远程调用挂起不放 | 设太短→正常慢请求被误杀;设太长→资源堆积 |
| 重试(Retry) | 瞬态网络故障、数据库死锁重试 | 重试风暴:下游已经很慢,重试再加重压力→雪崩 |
| 舱壁(Bulkhead) | 单个慢依赖耗尽线程池 | 隔离策略选错→资源利用率下降 |
| 熔断(CB) | 下游已不可用,避免无用等待 | 阈值太敏感→频繁开关→系统不稳定 |
关键建议
- 超时优先:所有远程调用(HTTP、RPC、DB 查询、消息发送)必须有超时。没有超时的系统等于没有韧性。
- 重试必须配合退避:固定间隔重试在高并发下=自残。用指数退避+抖动(Exponential Backoff + Jitter):
resilience4j: retry: configs: default: wait-duration: 200ms exponential-backoff-multiplier: 2 enable-exponential-backoff: true enable-randomized-wait: true # 抖动,避免惊群 - 熔断的状态转换:
CLOSED(正常) ↓ 失败率 > 阈值 OPEN(断开)—— 等待 wait-duration ↓ 超时后 HALF_OPEN(半开)—— 允许少量请求通过测试 ↓ 成功 → CLOSED ↓ 失败 → OPEN(重新计时) - 舱壁优先选信号量:Resilience4j 支持两种舱壁:
SemaphoreBulkhead(信号量,默认)和ThreadPoolBulkhead(线程池隔离)。微服务内部调用推荐信号量(不切换线程,开销小);涉及 IO 密集型且需独立线程池时选线程池。
架构视角:避免重试风暴
重试风暴是最危险的韧性误用之一。场景:
正常:客户端 → A → B → C(数据库)
故障:C 变慢,A 和 B 都配置了重试
→ 客户端重试 → A 重试 → B 重试 → C 压力 × 重试次数 × 链路深度
→ C 崩溃 → B 排队 → A 排队 → 所有服务雪崩治理原则:
- 只在最上游(入口网关/第一跳)配重试
- 重试前检查超时——已经超时 2s 的请求重试只会再等 2s
- 写操作(POST/PUT/DELETE)避免自动重试,除非下游做了幂等保障
- 使用
Retry+CircuitBreaker组合——熔断触发后停止重试
Resilience4j vs Spring Retry vs 自研
| 方案 | 适用场景 | 限制 |
|---|---|---|
@Retryable(Spring Retry) | 单个方法的重试(简单场景) | 不支持熔断、舱壁、超时 |
| Resilience4j | 完整韧性栈(推荐) | 需额外引入依赖,配置项多 |
| 自研拦截器 + Redis | 对 L4 扩展有统一治理平台需求 | 开发维护成本高 |
小结
- 韧性四模式:超时、重试、舱壁、熔断——超时是底线,熔断是兜底。
- Resilience4j 可与 Boot 3/4 无缝集成,配置化治理远程调用。
- 重试必须配合退避+抖动,且只在上游配置;写操作避免自动重试。
- 架构视角:韧性治理的根本不是工具,是确定每个依赖的超时与失败容忍度——需要在架构评审阶段明确每个外部调用的 SLA 属性(超时、可重试性、幂等性)。
- 思考任务:为项目中的远程 HTTP 调用(RestClient)配置 Resilience4j 超时 2s + 重试 2 次(退避 ×2)+ 熔断;用 JMeter 模拟下游 80% 错误率,观察熔断进入 OPEN 后的请求快速失败行为。
上一节:可观测性
下一节:模块化与系统边界
