超时与重试如何设计?别让一次故障变成请求风暴
超时是一份预算
一个请求调用多个下游时,不能给每个步骤都分配完整的用户等待时间。应先确定总截止时间,再按链路为连接、读取和处理留出预算,并把剩余期限传播给下游。上层已经放弃后,底层还无限运行,会继续占用连接和计算资源。
总超时与空闲读取超时也不是同一概念,流式接口尤其要区分。具体配置应依据正常耗时分布与业务容忍度,而不是复制一个固定秒数。
哪些失败可以重试
短暂连接失败、限流或部分服务端错误可以考虑重试,但仍要遵守接口语义。参数错误、认证失败和明确业务拒绝通常不应自动重试。HTTP 状态码只是线索,还要看对方契约和是否已经产生副作用。
读操作通常更容易安全重试;创建、扣款、发送等写操作需要稳定幂等键或结果查询。响应超时只意味着客户端没拿到结果,不意味着服务端没有执行。
为什么需要退避和抖动
服务故障时,大量客户端立即同步重试,会让恢复中的服务再次过载。指数退避逐步增加等待,抖动打散客户端节奏;同时设置最大次数和总时间,防止无止境循环。服务提供 Retry-After 等提示时,应结合剩余预算处理。
重试前要检查是否还有时间完成一次有意义的尝试。只剩极短预算时再发请求,往往只增加负载,无法改善用户结果。
多层重试怎样放大
网关、业务服务和 SDK 各自重试,会把一次用户操作变成许多下游请求。应明确主要重试层,其他层减少重复策略,并在日志中保留原始操作标识与尝试次数。不能只统计最终成功率,忽略系统为了成功付出的额外流量。
熔断、限流和排队可以处理特定过载问题,但不必在每个接口预先引入全部组件。先识别真实瓶颈,再选择足够的保护手段。
一个失败验证
在隔离环境让下游持续返回暂时性错误,检查请求是否按预算结束、重试是否有上限,以及并发负载是否被放大。再让写入成功但响应丢失,验证同一操作不会重复产生业务结果。取消后,连接和后台任务也应释放。
本文没有执行故障注入。面试时可以按“总预算、错误分类、有限退避、幂等与取消”回答,并说明超时参数如何从真实指标中获得。好的策略既争取恢复机会,也知道何时停止。