Skip to content

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: 10s

5.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

维度Resilience4jSentinelHystrix
生态Spring Cloud 官方Spring Cloud AlibabaNetflix(停维)
隔离策略信号量 + 线程池(可选)信号量线程池 + 信号量
熔断策略失败率 + 慢调用率 + 失败数失败率 + 慢调用比例 + 异常数失败率
限流有(RateLimiter)有(丰富)
重试有(Retry)
控制台无(需 Prometheus)Sentinel DashboardHystrix 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 秒)。