代表性面试主题

面试题:跨 RPC 调用如何传播 deadline 与取消信号?

后端困难
Offer.cc 编辑团队发布 更新

题干

客户端只等待 2 秒,服务 A 调用 B、B 再调用 C。如何传播 deadline 与取消信号,避免下游继续做已经无效的工作?

题干与适用场景

假设入口建立 2 秒用户预算,A 已耗时 500 毫秒后调用 B,B 还会调用 C 和非关键审计服务。说明 deadline、取消、重试和观测契约。时钟并不完全同步;只读 RPC 或带幂等键的命令才允许重试。目标是在保留序列化与传输余量的同时停止注定失败的工作。

面试官考察点

公开的 Staff 系统设计准备资料要求候选人澄清延迟、失败和规模,再解释组件边界与取舍。强回答应明确:入口只建立一个绝对截止时间;每跳计算剩余预算;取消沿调用树传播;重试受幂等性和剩余时间约束;指标能区分 deadline 超时、调用方取消、排队和取消后的无效工作。

回答前需要澄清的问题

  1. 2 秒是硬截止时间还是可接受的 SLO?硬截止时间意味着迟到结果无效;异步任务才可以继续。
  2. 哪些下游决定响应?审计、推荐可降级,授权不能省略。
  3. 写操作是否幂等?无保护副作用不能盲目重试。
  4. RPC 框架是否自动传播上下文?否则用拦截器统一实现并跨语言测试。
  5. 导出等任务是否必须完成检查点?若必须,改成有状态异步任务,不要让同步 RPC 忽略取消。

30 秒回答框架

“入口创建唯一 deadline,所有 RPC 从上下文计算剩余时间并预留响应余量。服务在入队和长任务循环中检查取消,重试只使用剩余预算且要求幂等。调用方断开或 hedge 有赢家时,取消沿调用树传播到所有分支。我会记录每跳剩余预算、排队时间、重试次数和停止延迟,并用慢下游、取消竞态和时钟偏差注入测试验证契约。”

分步骤深入解答

1. 统一预算表示

入口记录 t0 + 2s 的绝对 deadline,而不是让每跳重新增加 2 秒。服务计算 remaining = deadline - now,再扣除序列化和传输余量。gRPC 文档区分 deadline 与 timeout,并将传播值转换成已扣除耗时的剩余时间,以降低时钟偏差影响。应使用 RPC 上下文传递,不要依赖容易漏传的自定义字符串头。

2. 按关键路径花费预算

A 用掉 500 毫秒后,最多把 1.5 秒交给 B。若 B 需要 400 毫秒本地处理并保留 100 毫秒响应余量,调用 C 的预算不能超过剩余值和 C 的上限。并行分支共享父 deadline,不能各自获得完整的 1.5 秒。排队前先判断预计排队时间;已经来不及就快速拒绝或返回文档化的降级结果。

3. 传播取消

截止时间到期、客户端断开和 hedge 赢家都属于取消原因。子 RPC 继承父上下文,工作线程在入队前和长循环的有界间隔检查取消,释放许可并关闭流。Google SRE 指出只传播 deadline 仍可能泄漏调用树中的工作;下游失败后,取消应回传并扩散到兄弟分支。需要安全检查点的任务应改为独立异步作业。

4. 让重试服从预算

attempt_deadline = min(parent_remaining - response_margin, per_attempt_cap)。下次尝试无法在父 deadline 前完成时立即停止。只重试瞬时错误,且操作必须天然幂等或带幂等键。hedge 共享父预算,首个可接受响应返回后取消其他尝试。日志保留原始 deadline 和 attempt 编号,避免把濒临过期的重试误认为新请求。

5. 定义可观测失败

对 deadline 超时返回稳定状态(如 gRPC DEADLINE_EXCEEDED),保留原始取消原因。Trace 记录入口和出口剩余预算、排队耗时、尝试次数和取消到停止的延迟。用三跳测试验证:C 慢于剩余预算、B 排队时 A 取消、首个 hedge 成功、机器存在时钟偏差;断言子任务停止、重试终止且许可释放。

高质量示范回答

“我会让入口独占 2 秒 deadline。A 消耗 500 毫秒后把剩余预算放进 RPC 上下文交给 B,B 扣除响应余量后调用 C;并行分支共享父预算。每个子调用在入队和长任务中检查取消,客户端断开或 hedge 获胜时取消向下游扩散。重试必须幂等并受剩余时间限制。审计可以转成可追踪的异步作业,但关键响应不能把迟到工作算作成功。最后用慢依赖、排队、取消竞态和时钟偏差做集成测试,并在 trace 中显示每跳预算。”

常见错误

  • 每跳重新设 2 秒 → 调用链按跳数放大延迟 → 传播单一 deadline。
  • 只传播超时不传播取消 → hedge 和断开的请求继续占用线程 → 取消上下文扇出并测停止延迟。
  • 所有超时都重试 → 非幂等写入重复副作用 → 分类错误、限制尝试并要求幂等。
  • 先排队再检查预算 → 注定失败的请求占满容量 → 入队前拒绝或降级。
  • 固定余量没有数据 → 大响应持续失败或浪费预算 → 用传输和序列化分位数推导余量。

追问及应对

服务时钟不一致怎么办?

不要直接比较不同机器的墙上时钟。使用 RPC 框架的剩余时间转换,或传播扣除已耗时的 timeout,并在测试中注入时钟偏移。

下游能否延长 deadline?

同步响应不能延长;迟到结果已不符合父契约。需要继续处理时应改成可查询的异步任务。

如何给慢响应预留余量?

分别测量序列化和网络尾延迟,设置有界余量并限制载荷;余量长期吃掉预算时调整 SLO 或响应形状。

取消后哪些工作可以继续?

只有幂等且持久化的检查点或审计事件,并通过任务 ID 观测;不能继续占用同步连接和工作线程。

公开来源

同类题目