API请求失败太常见,重试机制怎么设才靠谱
做开发或者运维的都清楚,调用第三方API时网络抖动、服务短暂不可用几乎是家常便饭。你这边刚上线一个订单同步功能,结果用户反馈“提交成功但没到账”,一查日志发现是API返回了503错误。这时候光报错不处理,系统就卡住了。合理的重试机制,就是让程序在遇到临时故障时能自己“再试一次”,而不是直接放弃。
比如你在做一个支付回调通知系统,每次交易完成后要往商户服务器发一次POST请求。网络偶尔波动导致第一次发不出去,如果没有重试,那笔订单的状态可能就一直卡着。加个简单的重试逻辑,往往就能避免人工介入。
不是所有错误都值得重试
重试不是万能药。遇到400错误(参数错误)还一个劲重试,只会加重问题。真正适合重试的通常是临时性故障,比如:
- 503 Service Unavailable
- 504 Gateway Timeout
- 网络连接超时(如curl timeout)
- 限流返回的429状态码
这些情况大概率过一会儿就好了,重试才有意义。而像401未授权、404找不到接口这种,重试一百次也没用。
简单重试不如加点策略
最粗糙的做法是出错就立刻重试两次,这样容易在服务已经压垮的情况下继续雪上加霜。更稳妥的方式是加上延迟和退避策略。常见的有“固定间隔重试”和“指数退避”。
比如采用指数退避:第一次失败等1秒,第二次等2秒,第三次等4秒,最多重试3次。这样既给了服务恢复的时间,又不会让用户等太久。
function callApiWithRetry(url, maxRetries = 3) {
let retries = 0;
while (retries < maxRetries) {
try {
const response = fetch(url);
if (response.status === 200) return response;
// 临时错误才重试
if ([500, 502, 503, 504, 429].includes(response.status)) {
retries++;
const delay = Math.pow(2, retries) * 1000; // 指数退避
sleep(delay);
} else {
break; // 其他错误直接退出
}
} catch (error) {
retries++;
if (retries >= maxRetries) throw error;
const delay = Math.pow(2, retries) * 1000;
sleep(delay);
}
}
}上面这段伪代码展示了带指数退避的重试流程。实际项目中可以用现成的库,比如Python的tenacity,Node.js的retry,都能快速集成。
别忘了设置上限和熔断
重试次数太多,可能让原本轻微的问题演变成雪崩。比如某个下游API挂了,你的服务还在不断重试,堆积大量请求,最后把自己也拖垮。所以必须设上限:最多重试3次,总耗时不超过10秒。
更进一步可以加熔断机制。连续几次失败后,暂时停止调用,等一段时间再试探性恢复,类似家里跳闸后要先排查再合闸。这样能保护系统稳定性。
另外,每次重试最好记录日志,方便后续排查到底是哪一次成功了,还是全失败了。线上出问题时,这些日志就是第一手线索。
文档里写清楚,团队协作少踩坑
很多团队的API文档只写接口地址和参数,却不说明“这个接口支持重试吗?”、“建议重试几次?”。结果前端、后端、运维各自实现一套逻辑,有的重试有的不重试,问题定位起来特别麻烦。
理想的做法是在接口文档中标明:
- 是否支持幂等
- 推荐的重试次数
- 建议的退避策略
- 哪些状态码可重试
比如标注“本接口幂等,建议最多重试3次,间隔1/2/4秒”,别人对接时就知道该怎么处理。省得每次都要问一遍“这个错了能不能再发”。
说到底,重试机制不是写完就忘的功能点,而是系统健壮性的一部分。尤其是在网络环境复杂的今天,一次聪明的重试,往往比一堆报警短信管用。