bigywater.tech
微服务 · 14

降级:返回缓存或默认值的时机

依赖出问题时,返回缓存还是默认值?什么时候降级?降级本身会不会慢?过载时降级能换回多少容量?这篇用本机的小服务量出来。

100% → 0%
依赖不可用的 3 秒里,无降级与降级为缓存的报错比例;但缓存降级仍要等 203 ms 的超时,熔断 + 缓存是 0 ms
53 ms 对 608 ms
依赖有 5% 的 600 ms 长尾时,超时 50 ms(降级 3.2%)与超时 800 ms(不降级)的 p99
153/s → 300/s
到达 300/s 过载时,一直完整模式与切到简化模式的成功速率(66% 是简化结果)

依赖整段不可用

实测 · 本机执行输出
  做法      | 阶段  | 请求数 | 拿到真实结果 | 降级结果 | 报错   | 延迟中位数  | p99    | 降级用的缓存的平均年龄
  --------+-----+-----+--------+------+------+--------+--------+------------
  无降级     | 正常  | 200 | 100%   | 0%   | 0%   | 28 ms  | 32 ms  | -          
          | 故障中 | 300 | 0%     | 0%   | 100% | 502 ms | 504 ms | -          
          | 恢复后 | 300 | 100%   | 0%   | 0%   | 28 ms  | 33 ms  | -          
  降级为默认值  | 正常  | 200 | 100%   | 0%   | 0%   | 29 ms  | 32 ms  | -          
          | 故障中 | 300 | 0%     | 100% | 0%   | 203 ms | 204 ms | -          
          | 恢复后 | 300 | 100%   | 0%   | 0%   | 29 ms  | 32 ms  | -          
  降级为缓存   | 正常  | 200 | 100%   | 0%   | 0%   | 29 ms  | 32 ms  | -          
          | 故障中 | 300 | 0%     | 100% | 0%   | 203 ms | 204 ms | 1.9 s      
          | 恢复后 | 300 | 100%   | 0%   | 0%   | 28 ms  | 32 ms  | -          
  熔断 + 缓存 | 正常  | 200 | 100%   | 0%   | 0%   | 28 ms  | 32 ms  | -          
          | 故障中 | 300 | 0%     | 100% | 0%   | 0 ms   | 204 ms | 1.7 s      
          | 恢复后 | 300 | 77%    | 23%  | 0%   | 25 ms  | 33 ms  | 3.6 s      
  (连接类错误 0 次;不是超时,应为 0)

降级把报错变成可用的结果,但仍要等超时;熔断打开后才能立刻降级,恢复时又有一段时间继续降级。

超时与降级

实测 · 本机执行输出
  超时     | 请求数 | 降级的比例 | 延迟中位数 | p99    | 降级用的缓存的平均年龄
  -------+-----+-------+-------+--------+------------
  50 ms  | 600 | 3.2%  | 29 ms | 53 ms  | 0.55 s     
  100 ms | 600 | 3.2%  | 29 ms | 103 ms | 0.60 s     
  200 ms | 600 | 3.2%  | 29 ms | 203 ms | 0.70 s     
  400 ms | 600 | 3.2%  | 28 ms | 403 ms | 0.90 s     
  800 ms | 600 | 0.0%  | 29 ms | 608 ms | 0.00 s     
  (连接类错误 0 次;不是超时,应为 0)

超时越短,p99 越低,代价是用到的缓存更旧。

过载时降级

实测 · 本机执行输出
  做法              | 请求数  | 成功速率  | 成功的里面是简化结果的比例 | 成功请求的 p99
  ----------------+------+-------+---------------+----------
  一直完整模式          | 1500 | 153/s | 0%            | 371 ms   
  队列 ≥ 10 就切到简化模式 | 1500 | 300/s | 66%           | 63 ms    

用结果的完整度换容量:简化模式把容量翻倍。

分步演示

降级
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.8 / Go / nginx / Envoy / etcd / Consul / Nacos / OpenTelemetry / Prometheus(按各篇注明用到哪些及版本);所有服务都是本机自己起的进程,端口 9150 以上,只监听 127.0.0.1,不连任何外部服务,不碰本机已有的实例;故障用 kill -9 进程、让连接断开或在代码里注入延迟与错误来制造;计时用 time.perf_counter,数字每次运行会有小幅抖动。D 与第一节、第二节的“页面”是本进程里的 Python 服务和函数(HTTP 走环回);熔断器是第 5 篇里的手写版;缓存是进程内的字典,按用户保存最近一次成功的结果,没有 TTL;第三节的服务是 4 个线程 + 队列的模型,请求是进程内的对象;实验里连接类错误 0 次
    • 没有验证:缓存带 TTL / stale-while-revalidate 时的行为;降级结果的业务正确性(缓存的推荐是否还有意义);分布式缓存(Redis)的失败;降级开关的人工操作;每种配置只跑了一轮
    京ICP备2026067502号-1京公网安备11010502063487号