高可用五大手段

当依赖会挂、网络会抖、流量会爆——系统靠这五件事活下来:超时、重试、熔断、降级、限流。下面一次性讲清各自是什么、什么时候用、以及它们如何配合,每个都配了可动手玩的交互。

⏱ 超时 · 快速失败 🔁 重试 · 抗瞬时故障 ⚡ 熔断 · 及时止损 🛡 降级 · 保核心 🌊 限流 · 防被打爆
它们不是五选一,而是一条防线

一个请求从进来到返回,五件事依次(或同时)在保护它。看懂这条链路,就懂了高可用的骨架。

客户端 请求进来 限流 边缘拦截 超时 快速失败 重试 瞬时故障 熔断 持续故障 依赖服务 DB / RPC 降级:失败时返回兜底 ← 正常返回
限流:流量先过闸,超了直接拒 超时:等太久就放弃,别占着资源 重试:偶发失败再试一次 熔断:对方持续挂,干脆别调 降级:核心保住,非核心让路
一句话关系:限流挡住「量」,超时和重试处理「偶发」,熔断和降级处理「持续」。它们从外到内层层设防——越往外越便宜(越早拒绝越省资源)。
超时(Timeout):别让请求无限等

超时是高可用的第一道防线,也是最容易被忽视的一道。

是什么

调用一个依赖时,给它一个最大等待时间。超过这个时间还没返回,就主动中断、立刻失败(fail fast),把线程/连接/资源释放出来,而不是一直挂着。

两类超时

  • 连接超时:等多久还连不上对方(网络不通、端口没开)。通常设很短,如 1~3s。
  • 读超时 / 响应超时:连上了,等多久不返回结果。要参考接口 P99 延迟来设。

两个常见误区

  • 设太长:请求堆积,连接池/线程池被打满,连锁拖垮调用方。
  • 设太短:把本来能成功的慢请求「误杀」,反而制造失败。
  • 级联超时预算:调用链 A→B→C,必须 超时(A) > 超时(B) > 超时(C)。否则上游先断了,下游还在跑,白白浪费资源且拿不到结果。
经验值:超时一般设为「依赖 P99 延迟 × 1.5 ~ 2 倍」,并给整条调用链一个总预算(如 1s),再往下分摊。配合重试时,单次超时 × 重试次数 不能超过总预算。

🎮 交互:超时阈值怎么影响「成功 / 超时」

下面模拟 24 个请求,每个有真实响应耗时。拖动「超时阈值」看多少请求会超时;拖动「服务平均延迟」模拟依赖变慢。

800 ms
600 ms
🔁
重试(Retry):对抗瞬时故障

网络抖一下、依赖刚好 GC 了一下——这些「瞬时失败」值得再试一次。

是什么

请求失败后,按策略再发几次。对瞬时故障(超时、连接闪断、5xx 偶发)非常有效,能显著提升成功率。

三条铁律

  • 只重试幂等操作:GET、查询天然幂等;写/扣款要带幂等键(idempotency key),否则重试可能重复下单、重复扣款。
  • 只重试可重试的错误:超时、429、503 可重试;401(鉴权失败)、参数错误不要重试。
  • 要有上限 + 总预算:重试次数有限,且所有重试耗时必须小于超时总预算。

退避策略(别一股脑猛试)

  • 固定退避:每次隔固定时间,简单但易「重试风暴」。
  • 指数退避:间隔按 2ⁿ 增长(100ms→200ms→400ms),给依赖喘息时间。
  • 指数退避 + 抖动(jitter):在指数基础上加随机,避免大量请求同时重试造成「惊群」。这是生产推荐做法

🎮 交互:指数退避 + 抖动时间线

调节参数,看重试间隔如何随次数拉长;点「播放」逐个揭示每次重试的等待时刻。

100 ms
2.0
4 次
30%
熔断(Circuit Breaker):对方持续挂,就别调了

重试治「偶发」,熔断治「持续」。它像一个电闸,故障多了就跳闸,保护调用方不被拖死。

是什么 & 三状态机

  • Closed(闭合):正常放行,默默统计失败率。失败率超阈值(且请求数够多)→ 跳闸到 Open。
  • Open(断开):直接拒绝请求,不再调用依赖,快速失败,给故障方恢复时间。睡一段时间后 → Half-Open。
  • Half-Open(半开):放少量「探针」请求。成功 → 回到 Closed;失败 → 回到 Open。

熔断 ≠ 重试

  • 重试:认为「再试可能就通」,主动多试。
  • 熔断:判断「对方已经挂了,再试也是浪费」,主动不试。
  • 两者常配合:正常时重试抗瞬时;连续失败触发熔断,停止重试,避免雪崩。
雪崩场景:依赖变慢→线程池被占满→调用方自己也超时→上游继续重试放大流量→整条链路崩。熔断在「依赖持续异常」时切断这个正反馈。

🎮 交互:亲手跑一个熔断器

拖动「故障率」模拟依赖健康度,点「发送请求」观察状态切换;也可开「自动轰炸」连续发请求。

70%
CLOSED
正常放行中
0
成功
0
失败
0
被熔断拒
触发逻辑(本演示):滚动窗口内失败率 > 50% 且请求 ≥ 8 次 → Open;Open 维持 2.5s 后转 Half-Open,放行 3 个探针,全成功回 Closed,任一失败回 Open。
🛡
降级(Degradation):保住核心,牺牲边缘

当某个功能或依赖真的不可用,与其整体挂掉,不如「凑合着返回」,让核心链路先活下来。

是什么

在系统压力过大或依赖故障时,关闭或简化非核心功能,返回兜底数据/默认值/静态内容,保证主流程可用。

常见降级类型

  • 读降级:缓存/默认值代替实时查库(如展示「价格稍后更新」)。
  • 写降级:同步写改异步队列(如点赞先入队,稍后落库)。
  • 页面降级:动态页切静态页、关闭个性化推荐。
  • 功能降级:评论区挂了就隐藏,不影响主内容。

触发方式

  • 自动降级:由熔断、限流、负载阈值自动触发。
  • 手动降级:通过配置开关/预案,在大促或已知故障时人工开启。
  • 关键原则:核心链路(下单、支付)绝不降级;非核心(推荐、评论、积分)优先降级。

🎮 交互:正常 vs 降级,用户看到什么

选一个场景,切换「依赖是否正常」。故障 + 降级时,看系统如何用兜底数据保住体验。

场景说明

用户实际看到

🌊
限流(Rate Limiting):别让流量把系统冲垮

系统处理能力有上限。限流在「最外面」就把超出能力的流量拦下来,保住大部分请求。

是什么 & 四种算法

  • 计数器 / 固定窗口:每 1s 最多 N 个。简单,但有「窗口临界突刺」问题(59s 和 0s 各来 N 个,实际 2N)。
  • 滑动窗口:平滑统计最近一段时间,缓解突刺。
  • 漏桶(Leaky Bucket):请求进桶,按恒定速率漏出。强制平滑输出,超了就丢。削峰填谷。
  • 令牌桶(Token Bucket):按速率往桶里放令牌,请求要拿令牌才能过。允许突发(桶里攒的令牌可一次性用掉)。最常用。

限在哪、怎么响应

  • 维度:QPS、并发数、单用户、单接口、集群总入口。
  • 响应方式:直接拒绝(返回 429 Too Many Requests)、排队等待(漏桶)、或触发降级。
  • 令牌桶 vs 漏桶:令牌桶「能扛突发」,漏桶「强制匀速」。实际常组合:令牌桶限入口,漏桶/队列平滑后端。
为什么在最外层:越早拒绝,越省资源。限流挡掉的请求根本不会进业务系统,比在内部各个依赖处抢救便宜得多。

🎮 交互:令牌桶限流

桶容量 10,按设定速率补充令牌。点「发请求」消耗令牌,没令牌就被拒(429)。点「突发 20」模拟瞬时洪峰。

6 /秒
10
总结:一张表 + 一个请求的一生
手段解决什么问题触发后动作典型配置一句话
限流流量超过处理能力拒绝 / 排队令牌桶 QPS、并发上限挡住过量,保大部分
超时依赖响应过慢中断、快速失败连接/读超时、级联预算别无限等,释放资源
重试瞬时故障退避后重发幂等 + 上限 + 抖动再试一次可能就通
熔断依赖持续故障跳闸、停止调用失败率阈值、半开探针对方挂了就别调
降级功能/依赖不可用返回兜底开关、自动/手动保核心,舍边缘

🔚 一个请求的一生

请求到达 → 限流检查是否超量(超了直接 429)→ 进入调用,套上超时保护 → 遇到瞬时失败重试(带退避)→ 若依赖持续异常,熔断跳闸停止调用 → 任何失败都走降级返回兜底 → 核心链路始终可用。

落地口诀:外层限流挡量,调用加超时,瞬时靠重试,持续靠熔断,兜底靠降级;重试必须幂等,超时要有预算,熔断给半开探针,降级保核心舍边缘。