线上大促那晚,下游的订单服务因为数据库连接池被打满,响应时间从20毫秒一路飙到3.8秒。一开始只是单个服务变慢,但调用方用的是OpenFeign,默认超时和重试都偏乐观,于是上游服务开始堆积大量等待线程,接着上游线程池也满,再往上是网关——整个链路像多米诺骨牌一样往下倒。事后复盘翻出监控图,一行一行的 timeout 异常背后,其实是缺少一套能在依赖不可用时“主动断开”的机制。那之后我们才认真把 Sentinel 的熔断降级和系统自适应限流用起来。
这篇文章不讲宣传概念,只把 Sentinel 里最容易踩坑的熔断状态流转、降级规则参数、系统自适应限流算法思路,以及部署接入时的实际问题一次说清楚。适合已经跑过 Demo、准备上生产,或者正在排查线上限流问题的同学参考。
1. 熔断状态机的三个状态:为什么“半开”是设计精髓
1.1 从“快速失败”到“主动熔断”的一步之差
很多团队在引入 Sentinel 之前,对“保护下游”的理解就是调短超时时间、关掉重试。超时确实比无限等待好,但它解决不了一个问题:当依赖已经故障时,上游每个请求都会等满超时时间才失败,大量线程被卡在等待上,最终把自己的服务拖死。
熔断降级的本质,不是在请求“已经很慢”的时候做处理,而是在依赖“已经病了”的时候直接把流量挡住。打个比方:家里某个电器短路导致总闸跳闸,你不会在跳闸后反复推闸送电,而是会先断开所有负载,再把总闸推上去试一个回路。熔断器就是那个总闸,半开状态就是“推上去试一下”的动作。
Sentinel 的熔断器就是围绕三个状态设计的:CLOSED、OPEN、HALF_OPEN。
1.2 三个状态与计时规则
| 状态 | 含义 | 当前请求处理方式 | 离开条件 |
|---|---|---|---|
| CLOSED | 熔断关闭,正常放行 | 全部放行,同时统计响应时间/异常指标 | 统计窗口内指标达到规则阈值,进入 OPEN |
| OPEN | 熔断打开,直接拦截 | 所有请求直接拒绝,走降级逻辑 | 熔断时长 timeWindow 到期,进入 HALF_OPEN |
| HALF_OPEN | 半开探测 | 放行少量探测请求,其余仍拒绝 | 探测请求成功达到条件,恢复 CLOSED;否则重新 OPEN |
这里面有几个参数决定了状态流转的快慢,也是最容易踩坑的地方:
timeWindow:熔断时长,单位秒。它决定服务从 OPEN 到 HALF_OPEN 要等多久。设置太短,下游还在故障期就反复探测,造成“熔断-恢复-再熔断”的抖动;设置太长,下游已经恢复但流量还被挡着,影响业务恢复速度。minRequestAmount:触发熔断的最小请求数。这个参数很多人忽略。假设统计周期内只有 1 个请求,恰好这个请求超时了,如果没设最小请求数,就会直接触发熔断。在低流量接口上,这会引发“一次抖动就熔断十分钟”的乌龙。statIntervalMs:统计窗口时长。Sentinel 的实现是滑动窗口,窗口内的数据会滚动更新,不是简单的每秒重置。
半开状态是整个熔断器最值得琢磨的部分。Sentinel 在 HALF_OPEN 状态下并不是放行“最小请求数”个请求,而是通过一个 CAS 操作保证同一时刻只有一个探测请求能通过。如果这个请求执行成功且没有被慢调用判定命中,就把状态切回 CLOSED;如果探测请求失败或者超时,则立刻重新打开熔断器并重置计时。
这个设计为什么重要?因为它在“恢复服务”和“防止二次击穿”之间找到了平衡。如果半开时直接放行全部流量,下游可能刚恢复一点又被压垮;如果放行太少,恢复速度太慢。
1.3 和 Hystrix 的差异:隔离策略不是一回事
用过 Hystrix 的同学可能会问:Sentinel 的熔断器怎么没有线程池隔离?确实没有。Hystrix 的核心思路是给每个依赖分配独立线程池,线程池满了直接降级,用物理隔离阻断故障传播。但线程池隔离的代价是线程切换开销大、线程池参数难调,而且每个线程池中的线程是固定分配的,流量高峰期可能出现线程池空转但请求还是排队的情况。
Sentinel 的思路是“不隔离线程,但控制进入依赖的流量”。它更依赖熔断器本身来快速切断故障,同时默认所有保护都是基于信号量计数,没有线程池的调度开销。如果你的服务线程模型本身比较轻量,这种方案比 Hystrix 更容易保持高吞吐。
这一点在选型时要想清楚:Sentinel 适合追求高性能、对 RT 敏感的服务,Hystrix 适合需要严格线程隔离的强依赖场景。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 降级规则的三种熔断策略:参数背后是实际业务的取舍
2.1 慢调用比例:最常用但最容易误配
慢调用比例策略的核心判断是:在统计周期内,RT 超过指定阈值的请求占比达到设定值就触发熔断。它比单纯看“平均 RT”更合理,因为平均值很容易被几个极短请求拉低,掩盖了慢调用正在堆积的问题。
对应的核心参数如下:
| 参数 | 含义 |
|---|---|
| count | 慢调用阈值,即 RT 超过多少毫秒算慢调用 |
| slowRatio |
