雪崩预防:熔断 / 限流 / 降级
一句话速记
限流:控制进入系统的请求速率(令牌桶/漏桶),超限快速拒绝,保护后端;熔断:监控下游错误率,达到阈值则”断路”(停止调用下游,直接返回默认值),等待恢复后再试探;降级:主流程失败时,用低质量但可用的替代方案响应用户(返回缓存、默认值、静态页面)。三者组合是雪崩防御的标准套路:限流挡外部冲击,熔断保护内部依赖,降级保证系统可用。
关键细节
1)限流
令牌桶(Token Bucket)— 允许突发流量:
# 令牌桶:以固定速率往桶里放令牌,请求来了取令牌
# 有令牌 → 处理;无令牌 → 拒绝(或等待)
class TokenBucket:
def __init__(self, rate: float, capacity: int):
self.rate = rate # 令牌生成速率(个/秒)
self.capacity = capacity # 桶容量(允许的突发量)
self.tokens = capacity # 当前令牌数
self.last_refill = time.time()
def acquire(self) -> bool:
now = time.time()
elapsed = now - self.last_refill
# 补充令牌
self.tokens = min(self.capacity, self.tokens + elapsed * self.rate)
self.last_refill = now
if self.tokens >= 1:
self.tokens -= 1
return True # 允许
return False # 拒绝
# 优点:允许短暂突发(桶满时可以消费积累的令牌)
# 适用:API 限流,允许偶发的流量突增漏桶(Leaky Bucket)— 平滑输出:
请求进入漏桶 → 以固定速率流出(处理)
桶满 → 拒绝新请求
特点:输出速率恒定(无突发),适合保护下游服务(数据库等)
缺点:不允许突发,可能浪费下游的处理能力
Redis 实现限流(滑动窗口):
-- Lua 脚本(原子执行)
-- KEYS[1]: 限流 key(如 "rate:user:1001")
-- ARGV[1]: 窗口大小(秒)
-- ARGV[2]: 限流阈值
-- ARGV[3]: 当前时间戳(毫秒)
local key = KEYS[1]
local window = tonumber(ARGV[1]) * 1000 -- 转毫秒
local limit = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
-- 移除窗口外的旧记录
redis.call('ZREMRANGEBYSCORE', key, 0, now - window)
-- 当前窗口内的请求数
local count = redis.call('ZCARD', key)
if count < limit then
redis.call('ZADD', key, now, now) -- 记录本次请求
redis.call('EXPIRE', key, ARGV[1])
return 1 -- 允许
else
return 0 -- 拒绝
end限流框架:
// Sentinel(阿里开源,推荐)
@SentinelResource(value = "getUserInfo",
blockHandler = "handleBlock", // 被限流时调用
fallback = "handleFallback") // 业务异常时降级
public UserInfo getUserInfo(Long userId) {
return userService.query(userId);
}
public UserInfo handleBlock(Long userId, BlockException ex) {
return UserInfo.empty(); // 限流返回空对象(快速失败)
}
// 配置限流规则
FlowRule rule = new FlowRule("getUserInfo");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(1000); // QPS 上限 1000
FlowRuleManager.loadRules(List.of(rule));2)熔断(Circuit Breaker)
三态状态机:
CLOSED(正常):
正常调用下游
统计错误率 / 响应时间
达到阈值 → 转 OPEN
OPEN(断路):
停止调用下游(直接返回错误/降级结果)
等待 N 秒(冷却时间)→ 转 HALF_OPEN
HALF_OPEN(半开):
放行少量请求试探下游
成功 → 转 CLOSED(恢复)
失败 → 转 OPEN(继续断路)
Resilience4j(Java 熔断器):
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.slidingWindowType(SLIDING_WINDOW_TYPE.COUNT_BASED)
.slidingWindowSize(10) // 最近 10 次调用
.failureRateThreshold(50f) // 失败率 50% → 开路
.waitDurationInOpenState(Duration.ofSeconds(30)) // 开路等待 30s
.permittedNumberOfCallsInHalfOpenState(3) // 半开放 3 次试探
.build();
CircuitBreaker cb = CircuitBreaker.of("paymentService", config);
Supplier<PaymentResult> decoratedSupplier =
CircuitBreaker.decorateSupplier(cb, () -> paymentService.pay(request));
try {
return decoratedSupplier.get();
} catch (CallNotPermittedException e) {
// 熔断器开路,直接走降级
return PaymentResult.degraded("服务暂时不可用,请稍后重试");
}熔断的触发条件设计:
条件 1:错误率(Error Rate)
最近 N 次请求中,失败率 > 50%
条件 2:慢调用率(Slow Call Rate)
响应时间 > 1s 的请求占比 > 50%
→ 下游"看起来成功但实际很慢"也要熔断
条件 3:异常次数(Consecutive Failures)
连续失败 5 次 → 立即熔断
推荐:条件 1 + 条件 2 组合(兼顾速度和成功率)
3)降级
降级的层次:
Level 1:返回缓存数据(最优)
"商品详情"接口失败 → 返回 Redis 缓存的旧数据(可能 5 分钟前的)
用户几乎感知不到
Level 2:返回默认值 / 空数据
推荐系统失败 → 返回热榜商品(固定列表,预写入代码)
用户感知到"不个性化",但不崩溃
Level 3:返回友好提示
"当前服务繁忙,请稍后重试"
关键业务不能这样(如支付),非关键可以
Level 4:功能降级(关闭非核心功能)
评论功能挂了 → 商品详情页仍然显示,评论区显示"暂时不可用"
不影响购买主流程
多档位降级(T-k 日桶):
场景:每日跑批依赖昨日数据(T-1),但数据可能延迟
降级档位:
档位 0:使用 T-1 数据(最新,正常流程)
档位 1:T-1 数据未就绪 → 使用 T-2 数据(降一档)
档位 2:T-2 也不可用 → 使用 T-7 数据(降两档)
档位 3:所有历史数据不可用 → 使用固定基线数据(最保守)
配置化:
bucket.level=0 → 正常
bucket.level=1 → 告警 + 使用 T-2
触发方式:
自动:监控 T-1 数据就绪状态,自动切换档位
手动:紧急时运营同学在管控平台手动切档位
4)组合使用(雪崩防御体系)
外部流量 → 网关限流(Nginx/API Gateway)
→ 服务限流(Sentinel,业务粒度)
→ 熔断保护(Resilience4j,下游粒度)
→ 降级兜底(返回缓存/默认值)
监控告警体系:
限流次数 > 阈值 → 告警(可能被攻击/流量异常)
熔断次数 > N → 告警(下游服务故障)
降级触发 → 告警(影响数据质量)
延伸追问
- Q:限流和熔断的触发点有什么区别? → 限流在入口控制(控制进来多少请求),超限直接拒绝,保护本服务;熔断在出口控制(控制对下游的调用),下游异常时停止调用,保护下游和本服务(避免等待积累)。两者互补:限流防外部冲击,熔断防依赖故障传播。
- Q:Sentinel 和 Resilience4j 怎么选? → Sentinel:Java + Spring Cloud,功能更完整(限流+熔断+降级+系统保护+热点参数),有控制台(实时调整规则),国内互联网主流;Resilience4j:轻量级(纯函数式,无侵入),适合 Spring Boot 微服务,国外更流行。简单说:国内 Java 服务用 Sentinel,轻量化选 Resilience4j。
- Q:如何判断需要降级而不是报错? → 看业务影响:核心链路不可降级(支付、下单核心步骤);边缘功能可降级(推荐、评论、统计)。判断标准:降级后用户是否还能完成主要目标。降级设计是产品 + 技术共同决定的,需要定义”核心功能”和”非核心功能”的边界。
我的记法
- 限流:令牌桶(允许突发),漏桶(平滑),Redis 滑动窗口
- 熔断:CLOSED → OPEN(错误率达阈值)→ HALF_OPEN(试探)→ CLOSED
- 降级:缓存 → 默认值 → 友好提示 → 关闭非核心功能
- 组合:网关限流 + 服务限流 + 熔断 + 降级 = 雪崩防御四层
- 一句话:「限流挡洪水,熔断隔故障,降级保可用」
状态
- 已背速记
- 能画熔断器三态状态机
- 能说降级的四个层次