最新下载
热门教程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
如何设计一个具备“自动指数退避”重试逻辑的 API 轮询请求网关
时间:2026-07-21 11:19:49 编辑:袖梨 来源:一聚教程网
指数退避重试的核心是通过随机抖动、最大重试次数和超时熔断避免重试风暴与雪崩,而非简单增加重试次数;需区分4xx/5xx错误类型,仅对网络错误、5xx及Retry-After响应启用退避。
直接上结论:用指数退避重试网关,核心不是“多试几次”,而是让失败请求在系统压力大时主动让出资源,并避免雪崩——关键在退避策略必须带随机抖动、最大重试次数和超时熔断双保险。
为什么 setTimeout 简单累加会把后端压垮
常见错误是写成 retryDelay = base * 2 ** attempt 然后 setTimeout(fn, retryDelay)。这会导致所有客户端在同一时刻重试(比如第3次都在 800ms 后触发),形成“重试风暴”。尤其在服务短暂不可用后恢复的瞬间,大量请求扎堆打进来,可能直接击穿刚缓过来的后端。
实操建议:
- 必须加入 jitter:用
Math.random() * 0.3左右的随机因子,例如retryDelay = base * Math.pow(2, attempt) * (1 + Math.random() * 0.3) - 硬性限制最大退避时间(如
60000ms),防止某次重试卡住 5 分钟才动一下 - 每次重试前检查全局熔断状态,比如
if (circuitBreaker.isOpen()) throw new Error("CIRCUIT_OPEN")
fetch 请求中嵌入退避逻辑的最小可行实现
别封装成黑盒函数,要保留对 signal、headers 和响应体处理的控制权。下面这段可直接塞进你的请求工具里:
async function pollWithBackoff(url, options = {}, { base = 1000, maxRetries = 5 } = {}) { let lastError; for (let attempt = 0; attempt <= maxRetries; attempt++) { try { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), options.timeout || 10000); const res = await fetch(url, { ...options, signal: controller.signal }); clearTimeout(timeoutId); if (!res.ok) throw new Error(`HTTP ${res.status}`); return await res.json(); } catch (err) { lastError = err; if (attempt === maxRetries) break; const jitter = 1 + Math.random() * 0.3; const delay = Math.min(base * Math.pow(2, attempt) * jitter, 60000); await new Promise(r => setTimeout(r, delay)); } } throw lastError;}
注意点:
-
AbortController必须每次重试都新建,否则上一次 abort 会影响后续请求 -
timeout是 per-attempt 的,不是整个轮询周期的总超时 - 返回值是
await res.json(),不是原始Response,方便上层直接用数据;若需流式处理或自定义解析,应把res传出去
如何判断该重试 vs 该放弃(4xx/5xx 分类处理)
不是所有错误都适合指数退避。盲目重试 401 Unauthorized 或 404 Not Found 只会浪费资源;而 503 Service Unavailable、429 Too Many Requests、连接拒绝或超时才真正需要退避。
实操建议:
- 只对网络错误(
TypeError)、5xx和明确标了重试提示的响应(如Retry-Afterheader)启用退避 -
4xx中仅特殊处理408 Request Timeout和429;其余一律立即失败 - 如果响应头含
Retry-After,优先用它替代计算出的delay,但依然要叠加 jitter 防止同步 - 记录每次重试的
attempt、status、delay到日志,便于排查是否误判了错误类型
最易被忽略的是退避策略与业务语义的耦合:比如轮询订单状态,若 3 次重试后仍是 “PROCESSING”,该继续等还是通知用户“处理中”?退避逻辑只管请求节奏,状态语义必须由上层决定——网关不替你判断“多久算太久”。
相关文章
- 苹果折叠屏爆料汇总:售价超两万,比例阔折叠 07-30
- 纪念碑谷3 纪念碑谷3手游玩法详解与体验评测 07-30
- 晴空双子金卡阵容推荐 晴空双子高性价比氪金养成指南 07-30
- 大周列国志全新派系系统 07-30
- 兔小萌世界甜系小房间搭建指南 兔小萌世界高颜值甜系房间布置全流程详解 07-30
- 大周列国志全新剧本包西汉剧本包 07-30