重试为什么把故障放大
重试本身是提高成功率的手段,但在故障时会把流量成倍放大:一次失败被并发与重试次数相乘,下游接到的请求量可能远超正常水平,本来只是慢的服务直接被压垮,形成级联失败。识别特征很清晰——错误率与请求量同步陡增、TCP 连接数与新建连接数暴涨、TIME-WAIT 或 SYN-SENT 数量异常。处置的核心不是「少重试」,而是让重试有序:指数退避、随机抖动、重试预算与熔断。
基本信息
| 放大倍数 | 约等于并发数 × 重试次数,嵌套调用时按层相乘 |
|---|---|
| 正确姿势 | 指数退避 + 随机抖动(jitter)+ 重试预算或熔断器 |
| 适用前提 | 仅幂等操作可重试;写操作要么幂等化,要么不重试 |
| 观测指标 | 重试率、错误率、连接数与新建连接速率 |
| 适用层 | 应用 HTTP 客户端、网关 proxy_next_upstream、SDK 内置重试 |
典型使用场景
- 下游偶发超时后整体雪崩:确认是否存在无退避的同步重试
- 网关开启上游重试后错误放大:评估重试条件是否包含超时与非幂等请求
- 排查本机大量外连:出方向连接暴涨常是应用重试风暴而非正常流量
- 设计新接口的容错策略:为不同错误类型分别定义可重试与不可重试
- 故障复盘:把重试计数作为指标回溯,确认放大倍数与影响范围
识别特征
识别特征
ss -s | head -3 # 总览:连接规模是否异常
ss -Htan state syn-sent | wc -l # 大量半开说明在疯狂重连
ss -Htan state time-wait | wc -l # 短连接密集重试的典型残留
grep -rn 'retry' /etc/nginx/conf.d/*.conf 2>/dev/null | head # 检查代理层重试配置
ss -Htan state established | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn | head # 按目标看重试集中点
# 应用侧要确认三件事:重试次数、退避策略、可重试的错误类型是否限定在幂等范围重试策略对照
| 策略 | 效果 | 风险 |
|---|---|---|
| 立即重试(固定间隔) | 故障时形成同步脉冲流量 | 把下游从慢打成不可用 |
| 指数退避 | 给下游恢复时间 | 退避过短仍会堆积,过长影响成功率 |
| 指数退避 + 抖动 | 打散重试时刻,避免同步 | 成本极低,性价比最高 |
| 重试预算 | 把重试总量限制在可控比例 | 需要在系统中统一实现 |
| 熔断 | 下游持续失败时快速失败 | 阈值设置不当会误熔断 |
固定间隔重试在故障时会产生精确同步的脉冲流量,冲击比不做重试更严重;抖动是重试设计里最容易被忽略、也最关键的一环。