Skip to content

熔断、限流与降级

微服务架构中,服务链路的稳定性面临级联失败(Cascade Failure)的威胁:

正常调用链:
A → B → C → D(外部数据库)
D 响应缓慢(如数据库慢查询):
C 的线程池被 D 的慢响应占满
B 的线程池被 C 的超时积压占满
A 的所有请求全部超时
→ 整个链路全部不可用(雪崩)
根本原因:同步调用 + 无超时限制 + 无隔离机制
解决方案:超时控制 + 熔断 + 限流 + 降级 + 舱壁隔离

限流(Rate Limiting)控制单位时间内通过的请求数量,防止系统被超出处理能力的请求压垮。

时间分为固定大小的窗口(如每秒),每个窗口内维护计数器:
|--- 1秒 ---|--- 1秒 ---|--- 1秒 ---|
| 0~500 | 0~500 | 0~500 | ← 最多500个请求
问题:临界值突刺
0.9s 到 1.0s:499个请求(0~499计数)
1.0s 到 1.1s:499个请求(新窗口 0~499计数)
→ 0.1秒内通过了 998 个请求!是限制的 2 倍
解决固定窗口的临界值问题:
时间窗口随时间推移,始终统计最近 N 秒内的请求数
实现:
• 按细粒度桶(如100ms一个桶)存储计数,总窗口是多个桶的和
• 新请求到来时,丢弃窗口外的旧桶,累加窗口内所有桶的计数
Nginx 的 limit_req 模块使用滑动窗口
漏桶比喻:
请求是水滴,以任意速率流入桶
桶以固定速率漏出(处理请求)
桶满则丢弃(限流)
请求 →→→→→→ [ 桶 (容量N) ] → 固定速率 → 系统处理
桶满 → 丢弃
特点:
• 输出速率始终固定(平滑流量)
• 无法应对突发流量(桶满立即丢弃,即使系统有能力处理)
适用:对下游服务的保护(防止下游被突发流量压垮)
实现:队列 + 定时消费
入队:请求到来,入队(队列满则丢弃)
出队:定时任务固定速率出队处理
令牌桶比喻:
令牌以固定速率生成,放入桶中(桶有最大容量)
请求到来时消耗一个令牌,有令牌则放行,无令牌则拒绝/等待
令牌生成 → [ 令牌桶 (容量N) ]
请求 → 消耗令牌 → 系统处理 / 无令牌 → 拒绝
特点:
• 允许突发流量(桶内积累的令牌可以一次性消耗)
• 长期速率受令牌生成速率限制
• 既平滑了速率,又支持合理的突发
适用:接口限流(如 API QPS 限制)
Google Guava 的 RateLimiter、Sentinel 均使用令牌桶思想
对比项漏桶令牌桶
输出速率严格固定有令牌积累时允许突发
突发流量严格限制(桶满丢弃)支持(桶内令牌一次消耗)
适用场景保护下游(严格限速)接口限流(允许合理突发)

熔断器防止系统在依赖服务不可用时持续尝试,保护自身资源。

熔断器状态机:
CLOSED(关闭,正常状态)
↓ 失败率/慢调用率超过阈值
OPEN(打开,熔断状态)
↓ 等待 waitDurationInOpenState(如 60s)
HALF-OPEN(半开,探测状态)
↓ 允许少量请求通过
├── 请求成功率达到阈值 → 回到 CLOSED
└── 请求失败 → 回到 OPEN
CLOSED 状态(正常):
所有请求正常通过
统计失败率(最近 N 次 或 N 秒内)
失败率 > threshold(如50%)→ 切换到 OPEN
OPEN 状态(熔断):
直接拒绝所有请求(fast fail),立即返回降级结果
等待一段时间(给依赖服务恢复的机会)
超时后切换到 HALF-OPEN
HALF-OPEN 状态(探测):
允许有限数量的请求通过(探针请求)
成功率够高 → 切换回 CLOSED,恢复正常
失败 → 切换回 OPEN,继续等待

对比项HystrixSentinel
维护状态维护模式(仅修复 bug,不新增功能)活跃开发
隔离策略线程池隔离 / 信号量隔离信号量隔离
限流不支持(只有熔断)支持(QPS、并发线程数)
熔断策略基于失败率支持失败率、慢调用比例、异常数
规则动态配置代码配置,不支持动态支持多种数据源(Nacos、ZK 等)
控制台Hystrix Dashboard(功能弱)Sentinel Dashboard(实时监控,功能完整)
扩展性较弱Slot Chain 机制,高度可扩展
适用场景老项目维护新项目推荐
资源(Resource):
需要保护的目标(方法调用、URL、服务调用等)
规则(Rule):
流量控制规则、熔断降级规则、系统保护规则
Slot Chain(处理链):
NodeSelectorSlot → ClusterBuilderSlot → StatisticSlot
→ FlowSlot → DegradeSlot → AuthoritySlot → SystemSlot
每个 Slot 负责一种功能:
StatisticSlot:统计 QPS、线程数、响应时间
FlowSlot:流量控制(令牌桶/漏桶)
DegradeSlot:熔断降级(失败率/慢调用率)
SystemSlot:系统保护(CPU、内存、Load 阈值)

降级(Fallback)是当服务不可用时提供替代响应,保证用户体验。

常见降级策略:
1. 返回默认值/缓存数据
用户信息不可用 → 返回 {name: "用户", avatar: "默认头像"}
价格服务不可用 → 返回上次缓存的价格
2. 静态化 / CDN 兜底
动态页面服务不可用 → 返回预先生成的静态 HTML
3. 限制功能
推荐系统不可用 → 展示热门商品(不个性化推荐)
支付服务过载 → 降级为人工处理
4. 友好错误提示
非核心功能不可用 → 提示"该功能暂时不可用,请稍后重试"
核心功能不可用 → 引导用户到备用入口
设计原则:
• 降级的默认值必须对用户有意义(不能是乱数据)
• 记录降级事件(用于监控和告警)
• 核心链路不能被降级掉(支付流程不能因非核心服务降级而失败)

舱壁隔离来自船舶设计的”防水舱壁”:将不同服务的资源(线程、连接)隔离,防止一个服务的故障耗尽全部资源。

为每个依赖服务分配独立的线程池:
服务A 线程池(10个线程)→ 调用服务A
服务B 线程池(10个线程)→ 调用服务B
服务C 线程池(10个线程)→ 调用服务C
服务A 超时,耗尽了服务A 的10个线程
→ 不影响服务B、C 的线程池
→ B、C 调用继续正常
代价:线程数量 = 服务数量 × 每个服务的线程池大小,线程开销较大
为每个资源设置最大并发数(信号量):
当并发请求数 > 信号量值 → 拒绝多余请求
服务A:最大并发 20
服务B:最大并发 10
特点:
• 不创建额外线程,开销小
• 不支持异步调用(调用在当前线程内完成)
• 粒度比线程池隔离粗
优点:轻量;缺点:无法真正隔离(调用仍然在业务线程)

-- Redis + Lua 实现令牌桶(原子操作)
local key = KEYS[1] -- 限流 key
local capacity = ARGV[1] -- 桶容量
local rate = ARGV[2] -- 每秒生成令牌数
local now = ARGV[3] -- 当前时间戳(毫秒)
local requested = ARGV[4] -- 请求的令牌数
local last_tokens = tonumber(redis.call('hget', key, 'tokens') or capacity)
local last_time = tonumber(redis.call('hget', key, 'time') or now)
local elapsed = (now - last_time) / 1000.0 -- 经过的秒数
local new_tokens = math.min(capacity, last_tokens + elapsed * rate)
if new_tokens >= requested then
redis.call('hset', key, 'tokens', new_tokens - requested, 'time', now)
return 1 -- 允许
else
return 0 -- 拒绝
end

Q:令牌桶和漏桶算法有什么区别?

漏桶以固定速率漏出,输出严格平滑,无法应对突发(桶满直接丢弃);适合保护下游不被突发压垮。令牌桶以固定速率生成令牌,桶内可以积累令牌,允许突发流量(一次消耗积累的令牌);长期平均速率受令牌生成速率限制;适合接口限流场景。

Q:熔断器的三个状态是什么?如何转换?

CLOSED(正常)→ 失败率超阈值 → OPEN(熔断,直接拒绝)→ 等待 N 秒 → HALF-OPEN(探测,允许少量请求)→ 成功率高则回 CLOSED,失败则回 OPEN。

Q:Sentinel 和 Hystrix 有什么区别?

Hystrix 已进入维护模式,功能相对单一(主要是熔断+线程池隔离),不支持限流,规则不能动态配置。Sentinel 功能更丰富(限流+熔断+系统保护),规则支持动态配置(接入 Nacos 等),有完整的控制台实时监控,且仍在活跃开发,是 Spring Cloud Alibaba 的推荐选择。

Q:什么是服务雪崩?如何防止?

服务雪崩是一个服务的故障通过同步调用链级联扩散,导致整个系统不可用。防止手段:①超时设置(防止线程长时间阻塞);②熔断(检测到依赖不可用时快速失败);③限流(控制请求量,防止超载);④舱壁隔离(不同服务使用独立资源池,故障不扩散);⑤降级(提供替代响应,保证用户体验)。