超时设计

为每一次跨进程调用设置超时,让失败尽快暴露;阈值应覆盖调用链正常耗时并留余量,上游超时要大于下游预算,避免级联截断。

超时设计是什么

超时设计为每一次跨进程调用设置时间上限,让失败尽快暴露而不是无限等待。阈值应覆盖正常耗时的高分位并留出余量;预算从端到端目标逐层扣减,上游超时大于下游预算之和,否则上游提前放弃时下游还在继续执行,资源被白白消耗。

容易漏掉的等待

除了建连与读响应,连接池等待、域名解析、TLS 握手、序列化以及重试累计耗时都在预算之内。只设置读超时而忽略取连接的排队时间,高峰期同样会长时间阻塞,而且监控上看不到慢调用,因为请求根本没到下游。

基本信息

原则每个网络调用都要设超时,含连接与读写阶段
预算按端到端预算逐层扣减,而非各层等值设置
姿势超时后不再等待结果,同时要处理迟到的响应
迟到响应下游可能已执行完成,需要幂等或对账兜底

端到端预算拆分

端到端预算拆分
总预算 800ms(用户可接受)
├─ 网关与鉴权 50ms
├─ 订单处理 300ms
│   ├─ 库存查询 100ms
│   ├─ 支付预授权 150ms
│   └─ 校验与本地计算 50ms
├─ 其他下游调用 350ms
└─ 缓冲 100ms
# 规则:下游超时设置小于上游剩余预算,逐层扣减

各环节超时

环节是否容易遗漏说明
连接超时否建连阶段,通常设置得比读超时更短
读超时否等待响应阶段,按高分位加余量
连接池等待是池满时排队,需要单独设上限
域名解析与握手是首次调用或缓存失效时出现
重试累计时长是各次重试耗时之和不能超过上游预算

超时之后怎么办

超时只表示上游不再等待,不表示下游没有执行。写操作超时后必须能判断最终状态:靠幂等键查询、靠对账任务补偿,或让下游在超时后回写结果。读操作超时可以安全降级到缓存或返回默认值,写操作超时如果直接按失败处理,就会产生上游回滚、下游成功的状态分叉。

超时值应取自分位数据而不是平均值:平均值掩盖了长尾,按平均耗时设置的超时会在流量高峰大面积触发,表现为随机的接口失败。

易错点

  • 超时值取自平均耗时而非高分位,高峰期出现大面积超时
  • 上游超时小于下游处理时间,正常请求被上游提前取消
  • 只设读超时,连接池排队时间不受限制,高峰期线程大量阻塞
  • 超时后不处理迟到响应,下游实际执行成功但上游已按失败处理

数据来源与参考

  • Google SRE Book:Addressing Cascading Failures
  • gRPC 文档:Deadlines
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌