Appearance
Circuit Breaker 详解:熔断器
Spring Cloud Circuit Breaker 是 Spring Cloud 提供的熔断器抽象层,它定义了一套统一的熔断编程接口,让开发者可以用同一套代码在 Resilience4j、Sentinel、Hystrix 等不同熔断器实现之间切换。默认实现是 Resilience4j。
一、Circuit Breaker 是必须的吗?
技术上不是必须的,但生产环境上几乎是必须的。 没有熔断的微服务架构,就像没有刹车的汽车——平时能跑,一旦出事就是灾难。
┌──────────────────────────────────────────────────────────────────┐
│ 为什么说"几乎必须"? │
│ │
│ 微服务的本质是"分布式" │
│ → 分布式意味着"网络不可靠" │
│ → 网络不可靠意味着"下游随时可能挂" │
│ → 下游挂了 → 调用方线程阻塞等待 → 线程池耗尽 → 整个服务挂 │
│ → 整个服务挂 → 调用它的上游也挂 → 雪崩 │
│ │
│ 熔断器就是那个"刹车":下游出问题时,快速失败,保护自己 │
└──────────────────────────────────────────────────────────────────┘| 场景 | 需要熔断器吗? | 原因 |
|---|---|---|
| 单体应用,没有外部依赖 | ❌ 不需要 | 没有远程调用,不会雪崩 |
| 微服务但只有 2-3 个服务 | ⚠️ 建议有 | 挂了影响小,但加上了更安全 |
| 微服务,几十个服务互相调用 | ✅ 必须 | 没有熔断,雪崩是迟早的事 |
| 用了 Sentinel 做限流 | ⚠️ Sentinel 自带熔断 | 不需要单独引入 Circuit Breaker |
| 用了 Resilience4j | ⚠️ 可以单独用 | Circuit Breaker 抽象层可加可不加 |
一句话:Circuit Breaker 抽象层不是必须的,但"熔断能力"在微服务架构中是必须的。你可以用 Sentinel、Resilience4j 或 Hystrix 任意一个来实现熔断,Circuit Breaker 只是 Spring Cloud 提供的统一接口层。
二、为什么需要熔断器?
2.1 没有熔断器的灾难
order-service 调用 inventory-service(库存服务)
正常情况:
order-service → inventory-service → 返回结果(10ms)
异常情况:
inventory-service 故障(慢查询、死锁、网络抖动)
order-service → inventory-service → 等待 30s 超时
↑
每个请求都等 30s,线程池很快打满
→ order-service 也无法响应
→ 上游服务调用 order-service 也开始超时
→ 级联故障 → 雪崩2.2 熔断器的保护机制
order-service → Circuit Breaker → inventory-service
│
│ 监控调用结果
│
├── 正常:透传请求
│
├── 异常率 > 50%:熔断打开(快速失败)
│ 不调用 inventory-service,直接返回 fallback
│
└── 熔断一段时间后:半开(试探)
放少量请求试探,成功则关闭熔断,失败则继续熔断三、核心概念
3.1 熔断器状态机
┌──────────────────────────────────────┐
│ │
▼ │
┌──────────┐ ┌──────────┐
│ CLOSED │ 失败率/慢调用率 > 阈值 │ OPEN │
│ (正常) │ ──────────────────────→ │ (熔断) │
│ │ │ │
└──────────┘ └────┬─────┘
▲ │
│ 等待时间窗口到期
│ │
│ ▼
│ ┌──────────┐
│ 试探成功 │ HALF_OPEN │
└───────────────────────────── │ (半开) │
试探失败 │ │
(重新熔断) └──────────┘| 状态 | 说明 | 行为 |
|---|---|---|
| CLOSED | 正常状态,熔断器关闭 | 请求正常通过,统计失败率/慢调用率 |
| OPEN | 熔断状态,熔断器打开 | 拒绝所有请求,直接抛出 CallNotPermittedException |
| HALF_OPEN | 半开状态,试探恢复 | 允许少量请求通过,成功则关闭熔断器,失败则重新打开 |
3.2 三种熔断策略
| 策略 | 配置项 | 说明 | 触发条件 |
|---|---|---|---|
| 失败率 | failure-rate-threshold | 请求失败比例超过阈值 | 失败数/总请求数 > 50%(默认) |
| 慢调用率 | slow-call-rate-threshold | 慢调用比例超过阈值 | 慢调用数/总请求数 > 50%(默认) |
| 失败次数 | max-wait-duration-in-half-open-state | 固定失败次数 | 连续失败 N 次 |
四、快速上手
4.1 依赖
xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>4.2 配置
yaml
# 全局默认配置
resilience4j:
circuitbreaker:
configs:
default:
sliding-window-type: COUNT_BASED # 滑动窗口类型:COUNT_BASED / TIME_BASED
sliding-window-size: 10 # 窗口大小(10 次请求 / 10 秒)
minimum-number-of-calls: 5 # 最少调用次数(达到后才计算失败率)
failure-rate-threshold: 50 # 失败率阈值(50%)
wait-duration-in-open-state: 10s # OPEN 状态持续时间
permitted-number-of-calls-in-half-open-state: 3 # 半开状态允许的请求数
slow-call-duration-threshold: 3s # 慢调用阈值(超过 3s 算慢调用)
slow-call-rate-threshold: 50 # 慢调用率阈值
automatic-transition-from-open-to-half-open-enabled: true # 自动进入半开
record-exceptions: # 记录为失败的异常
- java.io.IOException
- java.util.concurrent.TimeoutException
instances:
inventoryService:
base-config: default # 继承默认配置
failure-rate-threshold: 30 # 覆盖:失败率阈值 30%
wait-duration-in-open-state: 5s # 覆盖:熔断 5 秒4.3 代码使用
java
@RestController
public class OrderController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/order/{id}")
@CircuitBreaker(name = "inventoryService", fallbackMethod = "fallback")
public Order getOrder(@PathVariable Long id) {
// 调用库存服务
String url = "http://inventory-service/inventory/" + id;
return restTemplate.getForObject(url, Order.class);
}
// fallback 方法:参数列表 + Exception 必须与原始方法一致
public Order fallback(Long id, Exception e) {
log.error("库存服务熔断,订单ID:{}", id, e);
return new Order(id, "降级数据", 0);
}
}4.4 编程式使用
java
@Service
public class OrderService {
@Autowired
private CircuitBreakerFactory<?, ?> circuitBreakerFactory;
public Order getOrder(Long id) {
CircuitBreaker breaker = circuitBreakerFactory.create("inventoryService");
return breaker.run(
() -> restTemplate.getForObject(url, Order.class), // 正常调用
throwable -> fallback(id, throwable) // 降级逻辑
);
}
private Order fallback(Long id, Throwable t) {
return new Order(id, "降级数据", 0);
}
}五、配合 Feign 使用
5.1 配置 Feign + Circuit Breaker
yaml
# 启用 Feign 的 Circuit Breaker 支持
spring:
cloud:
feign:
circuitbreaker:
enabled: true
resilience4j:
circuitbreaker:
instances:
inventory-service: # Feign 的服务名
sliding-window-size: 10
failure-rate-threshold: 50
wait-duration-in-open-state: 10s5.2 Feign Fallback
java
@FeignClient(name = "inventory-service", fallback = InventoryFallback.class)
public interface InventoryClient {
@GetMapping("/inventory/{productId}")
InventoryDTO getInventory(@PathVariable Long productId);
}
@Component
public class InventoryFallback implements InventoryClient {
@Override
public InventoryDTO getInventory(Long productId) {
return new InventoryDTO(productId, 0); // 降级返回库存为 0
}
}六、Resilience4j vs Sentinel vs Hystrix
| 维度 | Resilience4j | Sentinel | Hystrix |
|---|---|---|---|
| 生态 | Spring Cloud 官方 | Spring Cloud Alibaba | Netflix(停维) |
| 隔离策略 | 信号量 + 线程池(可选) | 信号量 | 线程池 + 信号量 |
| 熔断策略 | 失败率 + 慢调用率 + 失败数 | 失败率 + 慢调用比例 + 异常数 | 失败率 |
| 限流 | 有(RateLimiter) | 有(丰富) | 无 |
| 重试 | 有(Retry) | 无 | 无 |
| 控制台 | 无(需 Prometheus) | Sentinel Dashboard | Hystrix Dashboard |
| 动态配置 | 有限 | 控制台实时推送 | 有限 |
| 维护状态 | 活跃 | 活跃 | 停维 |
| 学习成本 | 中 | 中 | 低 |
选型建议:
纯 Spring Cloud 方案 → Resilience4j(官方推荐,轻量级)
Spring Cloud Alibaba 方案 → Sentinel(功能更丰富,有控制台)
老项目维护 → Hystrix(仅维护,不推荐新项目)七、滑动窗口原理
Resilience4j 使用计数型滑动窗口统计失败率:
窗口大小 = 10,已累积 10 次请求:
[✓] [✗] [✓] [✓] [✗] [✗] [✗] [✓] [✗] [✓]
← 旧 新 →
失败次数 = 5,失败率 = 5/10 = 50%
新请求到达(替换最旧的):
第 11 次请求 [✓] → 替换最旧的 [✓]
[✓] [✗] [✓] [✓] [✗] [✗] [✗] [✓] [✗] [✓]
↑ 被替换 ↑
失败率重新计算 = 5/10 = 50%(不变)失败率 ≥ 50% → 熔断器从 CLOSED 变为 OPEN
八、面试要点
Q1:熔断器三种状态及转换条件?
CLOSED(正常)→ 失败率/慢调用率超过阈值 → OPEN(熔断)→ 等待时间窗口到期 → HALF_OPEN(半开)→ 试探成功 → CLOSED,试探失败 → OPEN。
Q2:HALF_OPEN 状态的作用?
半开状态是熔断恢复的关键机制。它允许少量请求试探下游服务是否恢复,避免盲目恢复导致再次失败。如果试探成功则完全关闭熔断器,失败则继续保持熔断。
Q3:Resilience4j 和 Sentinel 怎么选?
纯 Spring Cloud 选 Resilience4j(官方推荐,轻量级);Spring Cloud Alibaba 选 Sentinel(功能更丰富,有 Dashboard 控制台,支持动态规则推送)。
Q4:熔断和降级的区别?
熔断是"断了"(拒绝调用不稳定的下游),降级是"有备选"(返回兜底数据)。熔断触发后通常执行降级逻辑(fallback),但两者是不同的概念:熔断是保护机制,降级是应变策略。
Q5:滑动窗口的作用?
滑动窗口用于统计最近一段时间的请求成功/失败情况,防止旧数据影响当前判断。COUNT_BASED 按请求次数(如最近 10 次),TIME_BASED 按时间(如最近 10 秒)。
