连载中 9/20

超时与重试:微服务调用的组合拳

2026-08-26 · 556 阅读 · 0 评论 · 0 赞

每层都设了 3 秒,然后呢

一次调用穿八层服务,每层超时都设 3 秒——听起来很整齐。最坏情况:第一层等 3 秒发现下游没回来,重试一次又是 3 秒,链路末端的理论等待能堆到几十秒;而入口处的用户,三秒前就已经关掉了页面。上游等不起的时候,下游的一切努力都是浪费——超时不是每层独立的事,是全链路的预算问题。

超时预算:层层衰减

把总超时当成一笔预算往下分:入口给 1 秒,网关留 0.9 秒,核心服务留 0.7 秒,最底层的 DB 查询留 0.2 秒——原则只有一条:下游的超时必须小于上游的剩余时间,否则下游做得再精确,上游早就放弃等待了。更精细的做法是超时传递:每一跳把已消耗的时间传下去,让下游拿到的不是静态值,而是本次请求的剩余预算。

层级预算说明
用户端1s入口总预算
网关0.9s预留 0.1s 给出口
核心服务0.7s重试必须装进预算
下游 RPC0.3s连接与读超时分开设
DB0.2s慢查询直接掐断

重试的三个前提

重试是把双刃剑,开之前确认三件事:幂等——只有读操作和幂等的写操作可以直接重试,非幂等写先做幂等化改造;预算——重试必须装进超时预算,读超时 3 秒配重试 3 次等于没预算;退避——立即重试等于撞枪口,指数退避加随机抖动把重试流量摊开。预算用尽就止损,不要让重试风暴沿着调用链向上滚

// 指数退避 + 随机抖动
long backoff(int attempt) {
    long base = 100L;                       // 基数 100ms
    long exp  = base << attempt;            // 100, 200, 400 ...
    long jitter = (long) (exp * 0.2 * Math.random()); // 20% 抖动
    return exp - jitter / 2 + random(jitter);
}
// 重试次数 1~2 次封顶,且必须在剩余预算内执行

和熔断的先后关系

重试和熔断是搭配使用的:重试解决偶发抖动,熔断解决持续性故障。次序很关键:重试发生在熔断器之内——失败率统计要包含重试失败的请求,下游真挂了熔断器才能基于真实失败率尽快打开;熔断打开期间重试直接短路,半开状态只放少量探测请求。反过来先重试后熔断,故障期流量先被放大一圈才被熔断,下游死得更快。熔断器的三态模型这里不展开,先记结论:先熔断保护,重试才有意义

一张默认值清单

给一套能直接抄的基线:连接超时 500ms~1s,建连不该超过 1 秒;读超时按接口 P99 的 2~3 倍定,不要拍脑袋统一 3 秒;重试 1~2 次封顶;退避基数 100ms、抖动 20%;所有参数进配置中心,支持运行时调整——超时是调出来的,不是定出来的。下一篇聊流量进来的第一站:API 网关

503

10 年全栈工程师 · 503咖啡馆主理人

#超时治理#重试#指数退避#超时传递#熔断

评论 (0)

相关推荐

连载中 12/20

排查四件套:jstack、jmap、jstat、jcmd 的实战分工

jstack 看线程在干什么,jmap 看堆里装了什么,jstat 看运行时在变什么,jcmd 是统一入口。四把刀各管一段,配合着用没有查不动的现场。

#jstack#jmap#jstat#jcmd#排查工具
2026-09-16 · 5 阅读 · 0 评论 · 0 赞
连载中 11/20

GC 日志:把回收过程翻译成人话

一行 GC 日志里塞着七种信息:谁触发的、收了哪、停了多久、活了哪些。加上 -Xlog 配置,再加上日志分析工具,GC 不再是只能盯监控曲线的黑盒。

#GC日志#Xlog#日志分析#GC监控#Full GC排查
2026-09-16 · 13 阅读 · 0 评论 · 0 赞
连载中 10/20

ZGC:亚毫秒停顿是怎么炼成的

百 G 大堆停顿不到一毫秒,靠的是把搬家全部挪到并发阶段——着色指针让引用自带状态,读屏障让搬运中的对象依然可访问。代价是吞吐与内存,收益是停顿与堆大小解耦。

#ZGC#着色指针#读屏障#亚毫秒停顿#分代ZGC
2026-09-15 · 8 阅读 · 0 评论 · 0 赞