跳转至

重试、退避与限速:失败时该怎么做

重试的目的不是“直到成功”,而是在临时网络问题下提高可靠性,同时避免给对方服务制造更大压力。对访问控制、明确拒绝或不符合规则的页面,重试不是解决办法。

1. 先区分失败类型

情况 推荐动作
DNS、连接或读取超时 有限次数重试,并逐次增加等待时间
429 Too Many Requests 停止高频请求,优先遵守 Retry-After,否则人工评估后延长等待
500502503504 可有限重试;仍失败则记录并结束该 URL
401403 不要尝试绕过;检查权限、条款或停止任务
404410 通常记录为不存在,不重试

HTTP 标准定义了 Retry-After 响应头,服务器可用它指示客户端何时再次尝试。没有该字段也不意味着可以立即反复请求。

2. 指数退避

一种常见策略是每次失败后增加等待:

from time import sleep

for attempt in range(3):
    try:
        response = fetch_response(url)
        break
    except TemporaryFetchError:
        if attempt == 2:
            raise
        sleep(2 ** attempt)  # 1 秒、2 秒;实际任务应再加入随机抖动

实际生产任务通常还会加入随机抖动(jitter),避免多个客户端同时重试。无论采用哪种策略,都应设置最大尝试次数和总任务上限。

3. urllib3 的 Retry 配置

urllib3.util.Retry 提供了连接、读取、重定向和状态码重试的配置对象。若在 Requests 中使用它,需要通过 HTTPAdapter 显式挂载;不要误以为 Requests 默认会自动重试所有失败。

配置时应谨慎限定:只对明确可安全重试的方法和临时错误状态启用有限重试,并保留正常的请求间隔。对于学习脚本,先使用单次请求加人工检查,通常比复杂自动重试更合适。

4. 限速始终独立于重试

即使请求全部成功,也要在请求之间等待。限速是正常行为;退避是失败后的额外保护,两者不能互相替代。

参考

评论