bigywater.tech
Spring · 127

Spring Cloud CircuitBreaker 与 Resilience4j:配置粒度

熔断器默认值是什么、为什么我的服务连续失败也没熔断?默认只等 1 秒的超时从哪来?按 id 怎么配?业务异常会不会触发熔断?id 用用户 ID 会怎样?这篇在 Spring Cloud CircuitBreaker 5.0.3 与 Resilience4j 2.3.0 上用真实的调用逐项测。

100
默认最少调用数,30 次连续失败窗口内失败率 100% 仍是 CLOSED
1000 ms
默认 TimeLimiter 超时,睡 1.5 秒的调用 1004 ms 后进 fallback
1000 个
每个请求用一个 id,调用一千个不同 id 后注册表里留下的熔断器数

默认值

实测 · 本机执行输出
  id=a:滑动窗口 COUNT_BASED 100,最少调用数 100,失败率阈值 50.0%,打开状态等待 60 秒,半开允许调用数 10;超时 1000 ms
  连续 30 次抛 IllegalStateException 的调用:{fallback:IllegalStateException=30},supplier 真正被调用 30 次,总耗时 2 ms,状态 CLOSED
  之后状态:CLOSED(窗口内调用 31,失败 30,被拒绝 0)
  一次睡 1.5 秒的调用:{fallback:TimeoutException=1},supplier 真正被调用 1 次,总耗时 1004 ms,状态 CLOSED

窗口 100、最少 100 次调用、失败率 50%、打开等待 60 秒、超时 1 秒。

Customizer

实测 · 本机执行输出
  id=orders:滑动窗口 COUNT_BASED 10,最少调用数 5,失败率阈值 50.0%,打开状态等待 2 秒,半开允许调用数 10;超时 3000 ms
  id=strict:滑动窗口 COUNT_BASED 4,最少调用数 2,失败率阈值 50.0%,打开状态等待 60 秒,半开允许调用数 10;超时 300 ms
  先在注册表里「偷看」id=peeked(没有经过 factory.run):先直接问注册表 CircuitBreakerRegistry 要 peeked:id=peeked:滑动窗口 COUNT_BASED 100,最少调用数 100,失败率阈值 50.0%,打开状态等待 60 秒,半开允许调用数 10;超时 1000 ms
  然后再通过 factory 调用并读取配置:id=peeked:滑动窗口 COUNT_BASED 100,最少调用数 100,失败率阈值 50.0%,打开状态等待 60 秒,半开允许调用数 10;超时 1000 ms

configureDefault 与按 id 的 configure;先问注册表会让配置失效。

状态机

实测 · 本机执行输出
  orders 连续 5 次失败:{fallback:IllegalStateException=4, fallback:CallNotPermittedException=1},supplier 真正被调用 4 次,总耗时 2 ms,状态 OPEN
  状态:OPEN(窗口内调用 5,失败 4,被拒绝 1)
  再调用 3 次(正常的 supplier):{fallback:CallNotPermittedException=3},supplier 真正被调用 0 次,总耗时 0 ms,状态 OPEN
  等 2.2 秒后调用 1 次:{ok=1},supplier 真正被调用 1 次,总耗时 2 ms,状态 HALF_OPEN
  状态:HALF_OPEN(窗口内调用 1,失败 0,被拒绝 0)
  再连续调用 9 次正常的 supplier:{ok=9},supplier 真正被调用 9 次,总耗时 1 ms,状态 CLOSED
  状态:CLOSED(窗口内调用 5,失败 0,被拒绝 0)
  strict(超时 300 ms)一次睡 1.5 秒的调用:{fallback:TimeoutException=1},supplier 真正被调用 1 次,总耗时 305 ms,状态 OPEN
  另一个 id=bad 连续 5 次抛 IllegalArgumentException(调用方传错参数这类业务异常):{fallback:IllegalArgumentException=5},supplier 真正被调用 5 次,总耗时 0 ms,状态 OPEN
  同一个 id 不同调用点共用一个熔断器:orders 已 OPEN 过之后 id=orders2 的状态 CLOSED(窗口内调用 0,失败 0,被拒绝 0)

OPEN 不调 supplier,等待后半开,凑满允许数后关闭;业务异常也算失败。

属性与 id 粒度

实测 · 本机执行输出
  配了 configs.default(窗口 7、最少 3 次)与 instances.byprop(窗口 3、最少 2 次)、timelimiter.configs.default.timeout-duration=2s:
    id=byprop:滑动窗口 COUNT_BASED 3,最少调用数 2,失败率阈值 50.0%,打开状态等待 60 秒,半开允许调用数 10;超时 2000 ms
    id=other:滑动窗口 COUNT_BASED 7,最少调用数 3,失败率阈值 50.0%,打开状态等待 60 秒,半开允许调用数 10;超时 2000 ms
  用 user-0 … user-999 一千个不同的 id 各调用一次:注册表里的熔断器个数 = 1000

resilience4j.* 属性有效;id 不要带用户或请求参数。

分步演示

CircuitBreaker
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK 21.0.12(Homebrew) / Spring Boot 4.0.8(Spring Framework 7.0.9、Tomcat 11.0.24) / Spring Cloud 2025.1.3;用 javac 编译、java 直接运行,依赖由 Maven 从 Maven Central 解析;数字每次运行会有小幅抖动。supplier 是本地代码(抛异常或 sleep);状态数据来自 CircuitBreakerRegistry;所有调用在同一个线程里顺序发出
    • 没有验证:超时之后 supplier 线程是否仍在后台继续运行;ignoreExceptions / recordExceptions 的配置;slowCallDurationThreshold 慢调用比例;时间窗口(TIME_BASED)滑动窗口;Bulkhead / 线程池隔离(Resilience4JBulkheadProvider);Reactive 版 ReactiveCircuitBreakerFactory;Micrometer 指标;Gateway 的 CircuitBreaker 过滤器;Sentinel 的对比(第 128 篇)
    京ICP备2026067502号-1京公网安备11010502063487号