微服务链路里,一个下游变慢就可能沿调用链向上传导,把上游的线程池和连接池全部拖垮。熔断负责在下游持续异常时快速失败止损,降级负责在资源紧张时主动舍弃非核心功能保住主链路,本文把两者的原理、状态机、落地方式与框架选型一次讲透。

它解决什么问题

一个请求在微服务系统里往往要穿越多个服务:A 调 B,B 调 C。假如 C 因为慢查询或 Full GC,响应时间从 50ms 涨到 3 秒,B 中等待 C 返回的线程就会不断堆积;B 的线程池耗尽后开始排队甚至拒绝,A 调 B 的线程跟着被占住,继续向上游蔓延。最后的现象是:真正出问题的只有 C,挂掉的却是整条链路,这就是服务雪崩。

雪崩的燃料有两个。一是资源没有隔离,下游的慢会直接占用上游的线程与连接;二是无节制的重试,客户端超时后立刻原样重发,请求量被放大数倍,本已濒临崩溃的下游被彻底压死。熔断与降级正是针对这两点的对策:熔断切断对异常下游的持续调用,降级主动收缩系统的服务范围。

一句话区分两者:降级回答”系统扛不住时先放弃什么”,熔断回答”下游已经病了还要不要继续调”。

核心原理

熔断器状态机

熔断器借鉴电路保险丝的思想,在三种状态之间循环:

        失败率 / 慢调用比例越过阈值
Closed ─────────────────────────────► Open
▲ │
│ 探测请求连续成功 │ 冷却时间结束
│ ▼
└────────────── HalfOpen ◄────────────┘
探测仍有失败则回到 Open
  • Closed(闭合):请求正常放行,熔断器在后台统计失败率与慢调用比例,指标越过阈值即跳到 Open。
  • Open(打开):请求不再发往下游,直接走 Fallback 快速失败。持续一段冷却时间(Hystrix 叫 sleepWindow,Resilience4j 叫 waitDurationInOpenState,通常为秒级)后进入 HalfOpen。
  • HalfOpen(半开):只放行少量请求试探下游。全部成功则回到 Closed 恢复流量;仍有失败说明下游尚未痊愈,回到 Open 重新冷却。

降级的层次

降级是有预案的主动取舍,常见四个层次:

  1. 返回默认值或空对象:推荐接口异常时返回空列表,价格预测失败时提示”暂无”。兜底数据的结构必须与正常响应完全一致,绝不能返回 null,否则上游直接 NPE。
  2. 读缓存兜底:返回最近一次成功的缓存结果,商品详情、用户信息这类读多写少的数据尤其合适;缓存也没命中就直接给默认值,不要再硬着头皮调下游。
  3. 功能裁剪:大促时关闭评论、积分、广告位等非核心模块,把 CPU、线程和带宽让给下单与支付链路。
  4. 写降级:非关键写入先落 MQ 或本地消息表,再异步同步到数据库,用最终一致性换取主链路可用。

深入机制

状态转换的工程细节

状态机看起来简单,工程细节才决定它是否可靠:

  • 统计窗口:熔断判断基于滑动时间窗内的聚合(例如 10 秒内慢调用比例超过 50%),而不是单请求。低 QPS 接口必须设置最小请求数门槛(如 Sentinel 的 minRequestAmount),否则一两个偶发超时就会造成误熔断。
  • 半开不等于恢复放量:HalfOpen 验证的是”下游是否痊愈”,而服务刚重启时缓存未热、连接池未建满,探测通过后也应配合 Warm Up 或限流缓慢放量,避免二次打挂。
  • Fallback 必须比主逻辑更快更简单:兜底里再发远程调用是典型的埋雷,一旦兜底依赖也挂掉,雪崩反而加剧。优先用本地缓存与静态默认值;必须访问 Redis 时也要用独立连接池和独立超时。

Sentinel 注解示例

@SentinelResource(
value = "queryStock",
blockHandler = "queryStockBlocked",
fallback = "queryStockFallback"
)
public StockVO queryStock(String skuId) {
return stockClient.query(skuId);
}

// 被限流、熔断、系统保护等 Sentinel 规则拦截时进入
public StockVO queryStockBlocked(String skuId, BlockException ex) {
return StockVO.unavailable(skuId);
}

// 业务方法自身抛异常时进入,签名与原方法一致并可追加 Throwable
public StockVO queryStockFallback(String skuId, Throwable t) {
return StockVO.unavailable(skuId);
}

blockHandler 承接的是规则拦截(BlockException),fallback 承接的是业务异常,两者同时配置时规则异常只进 blockHandler。参数校验失败、库存不足这类业务性结果应通过 exceptionsToIgnore 排除,不要统一兜底成”系统繁忙”,否则上游无法区分故障与正常拒绝。

从 Hystrix 到 Resilience4j 与 Sentinel

Hystrix 把线程池隔离、熔断、Fallback 这套模式普及开来,但早已进入维护模式,只修缺陷不再演进。它默认的线程池隔离把每个依赖调用切到独立线程池,隔离彻底,代价是线程上下文切换带来的尾延迟。

Resilience4j 是面向 Java 8+ 函数式风格重写的轻量容错库,按 CircuitBreaker、RateLimiter、Retry、Bulkhead 拆分模块,默认信号量隔离,按需引入即可。Sentinel 则把熔断作为流量治理的一环,支持慢调用比例、异常比例、异常数三种熔断策略,与限流、热点参数、系统自适应保护共用一套规则模型和控制台。

工程实践

  • 所有远程调用必须设超时:超时时间是熔断统计的输入,没有超时,熔断器永远等不到”失败”这个信号。
  • 降级开关走配置中心:用 Nacos、Apollo 动态下发,预案提前评审清楚触发条件、关闭哪些功能、返回什么、如何恢复,事故现场再改代码必然来不及。
  • 重试要有边界:最大次数加指数退避加随机抖动,写接口默认不重试。服务刚恢复时所有客户端同时重发会形成重试风暴,需要分批放量或用令牌桶限流削平。
  • 监控看尾部不看平均:平均值会掩盖少量极慢请求,熔断阈值参考 P99/P999 基线设定,恢复期间同时盯线程池活跃数、连接池占用和错误率。

对比与权衡

维度 Hystrix Resilience4j Sentinel
维护状态 维护模式,停止演进 活跃 活跃
定位 熔断降级组件 轻量容错库 流量治理平台
隔离方式 线程池 / 信号量 信号量(另有线程池 Bulkhead) 并发线程数控制(信号量思路)
熔断策略 以异常比例为主 异常比例、慢调用比例 慢调用比例、异常比例、异常数
配套能力 Dashboard/Turbine 已过时 模块按需组合 限流、热点参数、系统保护、控制台
适用场景 存量老系统 Spring Boot 3 / Java 17+ 新项目 Spring Cloud Alibaba / Dubbo 体系

熔断与降级的差异也值得单列:熔断是自动触发的运行时保护,对象是下游调用,目标是止损;降级多为预案驱动的主动决策,对象是功能与资源分配,目标是保住核心链路。再加上限流管入口流量,Fallback 则是三者触发后统一执行的兜底动作——限流拦入口,熔断断下游,降级保核心,Fallback 给结果。

面试视角

熔断器为什么需要半开状态? 直接恢复流量无法区分”下游已痊愈”和”下游仍在病中”,半开用少量请求做探针,成功才闭合。探测通过后也不应立刻全量,最好配合 Warm Up 缓慢放量,防止冷启动的服务被瞬时流量二次击垮。

降级和熔断的区别是什么? 熔断是面向下游依赖的自动保护:失败率或慢调用比例越阈值就断路,快速失败。降级是面向业务功能的取舍:资源紧张时按优先级关闭非核心能力、返回默认值或缓存。熔断常作为降级的触发器之一,降级的兜底结果通过 Fallback 落地。

Fallback 有哪些常见坑? 兜底逻辑里藏远程调用(依赖再挂雪崩加剧)、返回 null 导致上游 NPE、逻辑过重拖慢调用线程、多个业务共用一份缓存导致同时失效。原则是 Fallback 必须比主链路更快、更简单。

Sentinel 的 blockHandler 和 fallback 怎么分工? blockHandler 承接限流、熔断、系统保护等规则拦截抛出的 BlockException;fallback 承接业务方法自身抛出的异常。两者同时配置时规则异常只进 blockHandler,参数错误等业务结果应用 exceptionsToIgnore 排除。

为什么重试会放大雪崩? 下游慢导致超时,超时触发重试,请求量成倍放大,下游线程池更快耗尽,形成正反馈。所以重试必须有超时、次数上限、退避加抖动,并与熔断、限流协同使用。

小结

  • 雪崩的本质是故障沿调用链传导:慢下游占满上游线程,无边界重试进一步放大流量。
  • 熔断器用闭合、打开、半开三态在”保护下游”和”尽快恢复”之间自动切换;半开只负责探测,放量要靠 Warm Up。
  • 降级是预先设计的功能取舍,层次从默认值、缓存兜底到功能裁剪、写降级;开关要走配置中心,预案要提前演练。
  • Hystrix 已进入维护模式,新项目在 Sentinel(治理能力完整)与 Resilience4j(轻量模块化)之间按技术栈选择。
  • 记住分工:限流拦入口,熔断断下游,降级保核心,Fallback 给结果。