熔断与降级
一个依赖变慢,会拖着调用方一起变慢,再向上传导拖垮整条链路。本章回答一个问题,怎么在依赖故障时快速失败、返回兜底结果,而不是陪着一起等超时。它和 限流器 一起,构成服务容错的入口。
1. 级联故障是怎么发生的
服务 A 调用服务 B。B 变慢,A 的请求线程被占住等待 B 返回,A 的连接池和线程池逐渐耗尽,A 自己也变慢,调用 A 的上游跟着变慢。故障从 B 传导到 A,再传导到整个链路。
上游 ──▶ A ──▶ B(变慢)
│
A 的线程被占住等 B
│
A 的连接池耗尽,A 变慢
│
上游跟着变慢,链路崩溃根因在于,同步调用没有「快速失败」的开关。B 只是变慢,没有报错,A 就只能等,等到自己的资源被拖垮。要拦住这个传导,得在 A 这一侧装一个开关,B 出问题时直接短路,不再把请求发过去。
2. 熔断器状态机
熔断器(Circuit Breaker)就是那个开关。它有三种状态。
┌──────────────┐
│ CLOSED │ 正常放行,统计错误率
└──────┬───────┘
│ 错误率超过阈值
▼
┌──────────────┐
│ OPEN │ 直接拒绝,快速失败
└──────┬───────┘
│ 熔断窗口结束
▼
┌──────────────┐
│ HALF-OPEN │ 放少量探测请求
└──────┬───────┘
成功 │ 失败
┌────────┴────────┐
▼ ▼
CLOSED OPENCLOSED 是正常状态,请求照常放行,同时统计错误率。错误率超过阈值,熔断器切到 OPEN,之后一段时间内所有请求直接拒绝,不再发给依赖。OPEN 持续一个固定窗口后切到 HALF-OPEN,放少量探测请求试水,探测成功切回 CLOSED,失败重新回到 OPEN。
关键在 OPEN 期间「直接拒绝」。它让依赖有喘息的时间,也让调用方快速拿到失败结果,不再堆线程等超时。
3. 熔断、降级、限流是三个动作
三个词常被连在一起说,但它们是三个独立的动作,解决三个不同的问题。
熔断是开关,在依赖故障时自动打开,停止向它发请求。它保护的是下游,也保护调用方自己不被拖垮。
降级是返回兜底结果。熔断打开后,请求被拒绝,调用方不能什么都不返回,得给一个降级结果,比如返回缓存、返回默认值、返回友好提示。降级也可以不依赖熔断,大促时主动关掉非核心功能,也是一种降级。
限流是控制进入的请求量,超出容量的直接拒绝。它保护的是自身。
三者的关系是,限流和熔断都是「拒绝」,降级是「拒绝之后给什么」。熔断触发后通常走降级,但降级不一定由熔断触发。把这三个动作分清,才不会在配置里互相打架。
4. Resilience4j 落地
Resilience4j 是 Java 里做熔断的成熟组件。先引依赖,再用注解把熔断和降级挂在方法上。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-resilience4j</artifactId>
</dependency>@Service
public class OrderService {
@CircuitBreaker(name = "orderService", fallbackMethod = "queryOrderFallback")
public Order queryOrder(String orderId) {
return orderClient.query(orderId);
}
// 降级方法,签名和原方法一致,多一个 Throwable 参数
public Order queryOrderFallback(String orderId, Throwable t) {
return Order.fallback(orderId); // 返回兜底结果
}
}熔断的行为由配置决定。
resilience4j:
circuitbreaker:
instances:
orderService:
sliding-window-type: COUNT_BASED # 按调用次数统计
sliding-window-size: 10 # 统计最近 10 次调用
minimum-number-of-calls: 5 # 至少 5 次调用才开始计算错误率
failure-rate-threshold: 50 # 错误率达到 50% 触发熔断
wait-duration-in-open-state: 10s # 熔断 10 秒后进入半开状态几个参数的含义串起来是这样。sliding-window-size 是统计窗口,failure-rate-threshold 是触发线,minimum-number-of-calls 是统计生效的门槛,wait-duration-in-open-state 是熔断的冷却时间。只有调用次数达到 minimum-number-of-calls,错误率才算数,否则头几次调用失败就把错误率顶到 100%,熔断被过早触发。
5. 阈值怎么定
熔断配置的难点在 failure-rate-threshold 和 wait-duration-in-open-state 这两个值,它们决定熔断是「该开就开」还是「动不动就开」。
错误率阈值设太低,偶发抖动就触发熔断,服务来回切换状态,反而更不稳。设太高,真故障时迟迟不熔断,起不到保护作用。一般从 50% 起步,配合「最小调用数」过滤掉小样本抖动。
熔断窗口设太短,依赖还没恢复就又放请求进去,刚探通又被失败打回 OPEN,反复横跳。设太长,故障恢复后迟迟不放行,白白损失可用性。一般从 10 秒起步,按依赖的恢复速度调整。
这里没有标准答案,只有一条判断依据,熔断的目标是「快速失败、快速恢复」,阈值和窗口都要围绕「多久能恢复」来定。依赖重启要 30 秒,窗口就别设 5 秒。
6. 相关章节
限流的算法与选型见 限流器。降级作为高可用的一种手段,其方法论见 高可用。缓存场景里 Redis 宕机时的降级查询,见 多级缓存与纵深防御。