超时、重试与退避必须共同设计
说明超时怎样限定等待,重试怎样放大负载,以及退避、抖动和重试预算如何控制恢复。
超时、重试与退避必须共同设计
远程调用可能变慢、丢包、部分完成或彻底失败。超时限制调用方愿意等待多久,重试给临时故障一次恢复机会,退避和抖动控制重试流量何时再次到达。三者若分别使用默认值,会在下游过载时把小故障放大成级联故障。
超时是资源与用户期限的预算
没有超时的请求可能长期占用线程、连接、内存和上游会话。超时过短则把正常尾部延迟判为失败,引发额外重试。选择应基于用户可接受期限、依赖延迟分布、网络开销和调用链剩余预算,而不是固定复制一个数字。
连接超时、读取超时、写入超时和整个操作期限含义不同。只设置连接超时,连接建立后仍可能无限等待;每次读取都重置的超时,也可能让缓慢流持续占用资源。端到端期限更能约束一次业务动作总耗时。
上游期限应传递给下游。若入口只剩 200 毫秒,下游仍按 5 秒执行,最终结果已无人接收,还会浪费容量。每一层需预留处理与返回时间,不能把完整预算重复分配给所有依赖。
超时不等于操作没有发生
客户端等待超时,只能确定没有及时收到结果。服务端可能尚未开始、正在执行,也可能已经提交但响应丢失。对创建、扣款和发布等有副作用操作,盲目重试会重复执行。
幂等方法、幂等键和操作状态查询用于处理这种不确定。无法安全重试时,客户端应进入未知状态并查询或人工确认,而不是向用户显示明确失败后重新提交。
取消信号可以让服务端停止不再需要的工作,但并非所有数据库和外部调用都支持及时取消。系统仍要对迟到结果和部分提交负责。
重试只适合临时且可能恢复的失败
连接重置、短暂不可用和限流在某些条件下适合重试;格式错误、权限拒绝和业务冲突通常不会因等待而消失。服务端应使用清晰错误类别和可重试提示,调用方不能把所有非成功状态都当成临时故障。
HTTP 方法语义影响自动重试安全。GET 等幂等方法通常更适合重试,POST 需要业务幂等保护。即使方法幂等,大型请求重复执行也会消耗资源,仍需限制次数。
重试必须有总预算,包括最大尝试、总期限和允许的额外流量。无限重试会把永久故障变成永久负载,并阻止任务进入人工处理。
多层重试会成倍放大请求
若客户端、网关、服务和数据库驱动各重试三次,一次入口请求可能导致大量底层尝试。下游本已过载时,这些额外请求会降低恢复速度。重试责任应尽量集中在最了解业务结果和剩余期限的一层。
基础库可以处理连接级瞬时错误,但上层需要知道是否已经发生副作用。代理重试也应与应用幂等策略一致。架构文档要列出每层的超时和重试,而不是只检查业务代码。
重试预算可以按服务或客户端限制重试流量占正常请求的比例。当错误率升高时,主动减少重试比让所有请求各自耗尽次数更能保护系统。
指数退避降低同步冲击
立即连续重试会在依赖尚未恢复时再次施压。指数退避让等待时间随尝试次数增加,为系统恢复留出空间,并设置最大间隔防止无限增长。
如果大量客户端同时失败并使用相同退避间隔,它们仍会在同一时刻再次到达,形成周期性尖峰。抖动在等待时间中加入随机性,使重试分散。随机策略应记录范围并便于测试,不能在每层叠加无法预测的长延迟。
服务端 Retry-After 等提示可表达建议等待,但调用方仍要受自己的总期限和策略约束。提示缺失时不能假设立即重试最安全。
退化、限流和断路共同控制过载
重试不是唯一恢复手段。读取请求可以返回缓存或部分结果,非核心功能可以暂时关闭,限流可以拒绝超出容量的工作。断路器在持续失败时停止部分调用,避免每个请求都等待相同超时。
断路状态需要谨慎恢复。一次探测成功不代表依赖已恢复全部容量,集中放量可能再次压垮服务。半开探测、逐步恢复和容量信号应配合使用。
队列能吸收短峰,但积压会增加延迟。若任务价值随时间下降,应设置过期和丢弃策略,避免恢复后处理已无意义的旧工作。
尾部延迟决定用户体验
平均延迟无法指导超时。少数慢请求会占用大量资源,并主导高分位用户体验。超时应参考目标分位和不同请求类别,批量导出与交互查询可能需要不同预算。
对副本执行的幂等读取,有时可以在首个请求变慢后向另一副本发送对冲请求,以减少尾部延迟。但它会增加负载,必须限制比例,并确认副本一致性与取消行为。它不是普通写操作的默认方案。
冷启动、缓存未命中和大对象会形成合法慢请求,统一超时可能系统性伤害某类用户。监测需要按接口、负载大小和依赖拆分。
验证要覆盖故障期间而非正常路径
测试应注入延迟、丢包、响应丢失、限流和部分提交,观察尝试次数、总耗时与业务副作用。正常环境中重试几乎不发生,只有故障演练才能暴露多层放大和期限不一致。
生产指标需区分原始请求、重试、超时、取消和最终成功,并记录最老任务与连接池饱和。关联 ID 能把多次尝试归到同一业务动作,避免成功率被重复尝试扭曲。
合理策略应能回答:总期限是多少,哪些错误可重试,谁负责重试,是否幂等,采用何种退避和抖动,何时停止,失败如何恢复。超时、重试与退避只有作为一个系统设计,才会提高可靠性而不是放大故障。
资料依据
- Google:Site Reliability Engineering Books,https://sre.google/books/
- IETF:RFC 9110 HTTP Semantics,https://www.rfc-editor.org/rfc/rfc9110.html