跳到主要内容

超时预算与取消传播

超时用于限制一次等待,deadline 则规定整个调用必须在什么时间前结束。多层服务如果各自重新计算固定超时,最外层请求早已失败,内部任务仍可能继续排队、占用连接并写入结果。

1. 从用户请求建立总预算

入口根据接口 SLO 和调用方容忍度设置总 deadline。例如接口最多允许 800 ms,就要在其中容纳网关、服务处理、下游调用、序列化和返回响应。

预算不是把 800 ms 原样交给每个下游。当前服务先扣除已经消耗的时间和必要收尾时间,再把剩余 deadline 传播出去。并行调用可以共享剩余时限,串行调用则会依次消耗它。

2. 每种超时约束不同阶段

常见参数包括:

  • 连接池等待超时。
  • DNS、TCP 和 TLS 建立超时。
  • 请求写入与响应读取超时。
  • 数据库语句和事务超时。
  • 线程池或队列等待时限。
  • 整个 RPC 或 HTTP 调用 deadline。

只设置 socket read timeout,无法限制连接池排队、DNS 或多次重试的总耗时。应记录每一阶段,并由端到端 deadline 兜底。

3. deadline 沿调用链传播

RPC 元数据可以携带 deadline 或剩余 timeout。接收方取本地剩余时间继续向下传播,避免直接比较不同主机可能偏移的墙上时钟。

服务收到请求时若预算已经耗尽,应在不产生新副作用的前提下尽快拒绝。预算不足以完成一次最低成本操作时,也不应先开始再等必然超时。

内部后台任务若需要脱离原请求继续执行,应显式转成持久化作业,并返回任务 ID;不能悄悄忽略调用方取消。

4. 超时后要主动取消无效工作

调用方停止等待,不代表服务端和数据库自动停止。取消信号应传播到:

  • 尚未开始的队列任务。
  • 正在执行的 RPC 和 HTTP 请求。
  • 可取消的数据库语句。
  • 并行子任务和 Future。
  • 响应写回和资源清理。

执行代码需要在安全点检查取消,并正确处理中断。已经提交的数据库事务和外部副作用无法仅靠取消撤销,应通过状态查询、幂等和补偿处理。

5. 超时值来自延迟分布与错误成本

超时太短会把正常长尾请求误判为失败,并触发额外重试;太长则让线程、连接和调用方长期等待。可以从目标依赖的延迟分位数、网络差异和允许的误超时率建立初值,再通过压测和生产数据校准。

新连接的 TLS 握手、跨地域网络和冷启动会与连接复用后的请求有不同分布。一个统一的极小超时可能在发布或扩容时集中失败。

6. 服务端还要设置自身资源上限

即使调用方没有 deadline,服务端也不能无限执行。入口应设置最大请求时长,数据库和下游设置更短的内部上限,队列设置最大等待时间。

服务端上限是保护自身的最后边界;调用方 deadline 表达的是当前请求还是否有价值。两者应同时存在。

7. 指标区分主动取消与真实故障

至少记录:

  • 请求进入时的剩余预算。
  • 各阶段耗时和排队时间。
  • deadline exceeded、客户端取消和服务端超时。
  • 超时后仍继续运行的任务数量。
  • 取消成功率和已产生副作用的情况。

下游日志中的“客户端取消”可能是上游 deadline 正常触发,不应全部计为下游故障;但比例突然升高仍可能说明本层变慢或预算分配不合理。

8. 常见问题

8.1 超时后是否可以直接重试

先判断操作能否安全重复、剩余预算是否足够,以及原请求是否可能已经成功。写操作通常需要幂等键或状态查询;没有剩余时间时,重试只会增加负载。

8.2 Java 线程收到 interrupt 后会自动停止吗

不会。interrupt 是协作信号,代码和阻塞库要响应它。捕获 InterruptedException 后通常应恢复中断状态或结束任务,不能吞掉异常继续无限执行。

9. 面试题

9.1 多级服务调用如何设置超时并传播取消

出现公司:字节跳动

考察重点

  • timeout 与端到端 deadline 的区别。
  • 阶段预算、剩余时间和取消传播。
  • 已提交副作用与可取消任务的边界。

相关内容:第 1 节“从用户请求建立总预算”至第 7 节“指标区分主动取消与真实故障”。

参考回答

入口先根据接口目标设置端到端 deadline,每一层扣除已消耗时间和必要收尾时间,再把剩余预算传给下游,不能让每层重新获得固定超时。连接池、建连、读取、数据库和队列仍可设置各自更短的阶段超时,由总 deadline 限制全部阶段和重试之和。

调用方超时后还要向服务端、Future、数据库和下游传播取消,尽快释放不再有价值的连接与线程。取消无法撤销已提交事务或外部副作用,写操作仍需幂等和状态查询。监控要分别记录阶段耗时、进入时预算、主动取消和真正的依赖失败。