Resilience4j 入门:依赖引入、基本配置、创建熔断器实例

好,这一章咱们真正动手。Resilience4j 是什么?说白了就是一个轻量级的熔断限流库,专门给 Spring Boot 用的。我最早用 Hystrix,后来它停更了,就转到了 Resilience4j。嗯,感觉清爽很多——没有 Netflix 那一套厚重的依赖,配置也直接写在代码里,不搞什么注解魔法。

1. 依赖引入

先加 Maven 坐标。我用的是 Spring Boot 2.7.x + Resilience4j 1.7.1,注意版本适配,不然启动时会报 ClassNotFoundException。

<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-spring-boot2</artifactId>
    <version>1.7.1</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

如果你是 Gradle 项目,对应的写法是:

implementation 'io.github.resilience4j:resilience4j-spring-boot2:1.7.1'
implementation 'org.springframework.boot:spring-boot-starter-aop'
个人小建议: 别直接用最新版,先去 Maven Central 看有没有已知的 Bug。我踩过一次 1.7.0 的坑,CircuitBreaker 状态切换日志丢失,升到 1.7.1 才修复。

核心依赖就这两个。resilience4j-spring-boot2 会帮你自动装配 CircuitBreaker、RateLimiter、Retry 等组件,配合 @CircuitBreaker 注解开箱即用。starter-aop 是必须的,因为注解底层靠 AOP 拦截。

2. 基本配置

Resilience4j 支持两种配置方式:全局默认配置和实例独享配置。我个人习惯在 application.yml 里统一定义,再给关键调用单独覆盖。

下面是一个完整的配置模板:

resilience4j.circuitbreaker:
  configs:
    default:
      sliding-window-size: 10              # 滑动窗口大小
      minimum-number-of-calls: 5           # 最少调用次数(达到后才计算熔断)
      failure-rate-threshold: 50           # 失败率阈值(%)
      wait-duration-in-open-state: 5s      # 进入 OPEN 后的等待时间
      permitted-number-of-calls-in-half-open-state: 3  # HALF_OPEN 时允许的调用数
      automatic-transition-from-open-to-half-open-enabled: true  # 自动从 OPEN 转 HALF_OPEN
  instances:
    orderService:
      base-config: default
      failure-rate-threshold: 30           # 订单服务更敏感,失败率超过30%就熔断
    paymentService:
      base-config: default
      wait-duration-in-open-state: 10s     # 支付服务熔断后等久一点

每个参数什么意思?我整理了一张速查表:

配置项 默认值 说明
sliding-window-size 100 滑动窗口内记录的调用次数,窗口越大统计越准,但内存消耗也越大
minimum-number-of-calls 100 至少请求多少次后才开始计算失败率。我一般设小一点(5或10),方便测试
failure-rate-threshold 50 失败率达到多少百分比时熔断。线上建议 60-80,避免误伤
wait-duration-in-open-state 60s 熔断打开后等待多少秒才尝试放行。我习惯设 5-10s,太长会影响恢复速度
permitted-number-of-calls-in-half-open-state 10 半开状态允许通过的测试请求数。设太少容易抖动,设太多又会压垮下游
automatic-transition-from-open-to-half-open-enabled true 是否让熔断器自动从 OPEN 转为 HALF_OPEN。建议开启,不要手动干预

3. 创建熔断器实例

有了配置,就可以在代码里创建实例了。Resilience4j 提供两种用法:注解式和函数式。我先讲注解式,因为团队里大部分同事都用它,上手快。

在 Service 方法上加 @CircuitBreaker:

@Service
public class OrderService {

    @CircuitBreaker(name = "orderService", fallbackMethod = "fallback")
    public Order getOrder(Long orderId) {
        // 调用远程服务或数据库
        return restTemplate.getForObject("http://order-db/orders/" + orderId, Order.class);
    }

    public Order fallback(Long orderId, Throwable t) {
        // 降级逻辑
        log.warn("熔断触发了,orderId={}", orderId, t);
        return Order.empty();
    }
}

注意 fallback 方法的签名要和原方法一致,多一个 Throwable 参数。我曾经因为参数顺序写反,一直没触发 fallback,排查了一下午……嗯,记住原方法参数在前,Throwable 在最后。

如果你不想用注解,也可以用编程式创建 CircuitBreaker 实例:

CircuitBreakerConfig config = CircuitBreakerConfig.custom()
    .slidingWindowSize(10)
    .failureRateThreshold(50)
    .waitDurationInOpenState(Duration.ofSeconds(5))
    .build();

CircuitBreakerRegistry registry = CircuitBreakerRegistry.of(config);
CircuitBreaker breaker = registry.circuitBreaker("orderService");

Supplier<Order> decorated = CircuitBreaker.decorateSupplier(breaker, 
    () -> restTemplate.getForObject("http://order-db/orders/{id}", Order.class, 1L));

Order result = Try.ofSupplier(decorated)
    .recover(throwable -> Order.empty())
    .get();

编程式更灵活,适合做单元测试或动态调整。不过平时我建议先注解式,出现问题再转编程式查细节。

来看一张熔断器的状态切换图,理解了这张图,所有配置就都串起来了:

Resilience4j 熔断器状态机 CLOSED 正常调用,统计失败率 OPEN 直接拒绝,等待超时 HALF_OPEN 放行少量请求试探 失败率 ≥ 阈值 等待超时后自动转 成功率达标 → 恢复 仍失败 → 重新打开 💡 关键配置点:sliding-window-size 影响 CLOSED 统计精度;wait-duration-in-open-state 决定 OPEN 持续时间;permitted-number-of-calls-in-half-open-state 控制试探并发量

4. 验证与调试

配置写完了,怎么确认它在工作?我的做法是写一个简单的 JUnit 测试,用 Mock 让远程调用一直抛异常,观察状态切换日志。

@SpringBootTest
class OrderServiceTest {

    @Autowired
    private OrderService orderService;

    @Test
    void testCircuitBreakerOpen() {
        for (int i = 0; i < 20; i++) {
            try {
                orderService.getOrder(1L);
            } catch (Exception ignored) {
            }
        }
        // 检查熔断器状态
        CircuitBreaker breaker = CircuitBreakerRegistry.ofDefaults()
                .circuitBreaker("orderService");
        assertThat(breaker.getState()).isEqualTo(CircuitBreaker.State.OPEN);
    }
}
要注意: 我第一次测的时候总不熔断,后来发现是 minimum-number-of-calls 设了 100,我只调了 10 次。把值改成 5 就正常了。另外记得在测试配置里关闭缓存,否则状态不会被更新。

另外,启动时加 --debug 可以看到 Resilience4j 的自动配置日志,确认你的实例名是否和配置里的匹配。名称不匹配会使用默认配置,有些坑就是在这里埋下的。

5. 一个小结

这一章我们不谈复杂场景,先把依赖、配置、实例创建这三步走通。你想想看,熔断器的核心其实就是一个状态机,配好滑动窗口大小和阈值,剩下的交给 Resilience4j 自动管理。我当年的第一个生产熔断配置就是随手写了个 50% 阈值,结果流量峰值时疯狂 OPEN/CLOSED 震荡,后来加了 minimum-number-of-calls 和合理的 wait-duration 才稳下来。嗯,先动手配一个试试,比什么都强。