用 Python 调用云服务 API 时,把重试和超时写对:一次可直接复用的实战清单
很多线上问题并不是接口本身写错了,而是调用方没有把超时和重试写对:一次网络抖动就让请求挂几十秒,或者一次重试把本已成功的下单又执行了一遍。本文用 Python 的 requests 举例,给出一套可以直接复用的做法,核心结论只有一句:超时要分开设,重试只对可重试的失败,写操作必须先保证幂等。
一、超时:连接超时和读取超时要分开
requests 的 timeout 如果只传一个数字,它既是连接超时也是读取超时,实际场景里两者应该不同。连接超时可以短一些,读取超时要给服务端留出正常处理时间。一个常用起点是 timeout=(3, 10),表示 3 秒连不上就放弃,连接成功后 10 秒内读不到响应也放弃。千万不要不设超时,否则一个挂起的请求可能拖垮整个线程池;也不要把超时设得过大,否则重试和排队都会被放大。
二、重试:先分清哪些失败可以重试
可以重试的失败通常是:连接异常、读取超时、502、503、504,以及 429 限流。不要重试的失败通常是:400 参数错误、401 鉴权失败、403 权限不足、404 资源不存在,这些重试多少次结果都一样,只会浪费配额。特别要注意写操作:创建订单、扣减库存、发起支付这类操作,即使服务端返回超时,也不能断定服务端没有执行,盲目重试可能造成重复执行。
三、退避:不要固定间隔重试
如果所有客户端都在同一时刻重试,服务端刚恢复又会被打垮。正确做法是指数退避加随机抖动,例如第一次等 0.5 秒左右,第二次等 1 秒左右,第三次等 2 秒左右,每次再加一个随机的小抖动。重试次数也要封顶,一般写操作不超过 2 次,读操作不超过 3 次,总耗时要有一个明确上限,超过上限就快速失败并告警,而不是无限重试。
四、写操作:幂等键是前提
对于不能重复执行的操作,调用方应该在请求头里带一个幂等键,例如 Idempotency-Key,同一个幂等键重复提交时服务端只执行一次。客户端生成幂等键时,要用业务单号加操作类型组合,而不是每次重试都生成一个新值,否则幂等就失效了。如果服务端不支持幂等键,稳妥的做法是:超时后先用查询接口确认结果,确认不存在再重发,而不是直接重发写请求。
五、限流响应:尊重 Retry-After
遇到 429 时,先看响应头里有没有 Retry-After,有就按它等待,没有再用自己的退避策略。不要在限流时立即重试,也不要把限流当成普通错误无限循环。把 429 单独计数并告警,往往能提前发现调用量突增、配额不足或者某个循环调用写错了。
六、一个可直接复用的调用骨架
思路是:封装一个统一的调用函数,内部处理超时、重试、退避和日志,业务代码只传方法和参数。伪代码如下:先定义可重试的状态码集合和异常类型,再循环尝试;每次请求都带上连接超时和读取超时;遇到可重试失败时按退避等待并记录;遇到不可重试失败时立即返回;超过最大次数后抛出统一的调用异常,业务层再决定是降级、排队还是返回友好提示。把这套逻辑集中在一个地方,比每个业务各自写 try 更容易维护,也更容易统一调整超时和重试策略。
七、日志和观测:重试不能是黑盒
每次重试都应该记录:请求方法、地址、状态码、耗时、重试次数和幂等键。只记录“最终失败”是不够的,因为重试成功也会掩盖问题。建议把重试率、超时率、429 次数作为日常指标,一旦重试率突然上升,先查服务端和网络,而不是先调大超时。还可以在日志里区分“首次成功”“重试后成功”“最终失败”,这样复盘时能快速判断是偶发抖动还是持续故障。
八、上线前的检查清单
第一,超时是否分开设置,是否所有请求都有上限;第二,重试集合是否只包含网络类和 429、502、503、504;第三,写操作是否有幂等键或先查询再重发;第四,退避是否带抖动,重试次数和总耗时是否封顶;第五,日志是否能看到每次重试,指标是否能报警。把这五条过一遍,绝大多数“接口偶尔卡住”“重复下单”“限流后雪崩”的问题,都能在上线前被挡住。
- 点赞
- 收藏
- 关注作者
评论(0)