如何在Python Retry中获取有意义的HTTPError异常?
解决requests重试后抛出RetryError而非HTTPError的问题
异常差异说明
首先明确三个异常的本质区别:
urllib3.exceptions.MaxRetryError:urllib3底层重试机制抛出的异常,代表所有重试尝试全部失败,没有达到重试终止的成功条件(比如一直返回500直到重试次数耗尽)。requests.exceptions.RetryError:requests对上述底层异常的包装,把urllib3的异常转换成requests体系内的异常类型。requests.exceptions.HTTPError:当请求成功拿到响应,但状态码属于4xx/5xx范围时,调用response.raise_for_status()触发的异常——此时请求已经完成,只是结果不符合业务预期。
你的场景中,重试机制耗尽次数后直接抛出重试相关异常,而非返回最后一次的500响应再触发HTTPError,原因是默认的重试配置会在失败时终止请求并抛出重试异常,不会返回最后一次的响应。
解决方案
有两种简洁的方式实现你想要的效果:
方案1:修改Retry参数,返回最后一次响应后手动触发HTTPError
通过设置raise_on_status=False,让重试机制在耗尽次数后不抛出RetryError,而是返回最后一次请求得到的响应,之后调用raise_for_status()就能和直接请求一样抛出HTTPError:
import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry session = requests.Session() # 配置重试规则,关键开启raise_on_status=False retry_config = Retry( total=3, # 总重试次数 status_forcelist=[500], # 需要重试的状态码 allowed_methods=["GET"], # 允许重试的请求方法 raise_on_status=False # 重试耗尽后返回最后一次响应,不抛出RetryError ) adapter = HTTPAdapter(max_retries=retry_config) session.mount("http://", adapter) session.mount("https://", adapter) # 发送请求,后续触发HTTPError response = session.get("http://example.com/500") response.raise_for_status() # 这里会抛出requests.exceptions.HTTPError
方案2:自定义HTTPAdapter,自动处理RetryError并抛出HTTPError
如果不想在业务代码中手动调用raise_for_status(),可以自定义Adapter,在内部捕获RetryError并提取最后一次响应,触发HTTPError:
import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry class RetryAwareAdapter(HTTPAdapter): def send(self, request, stream=False, timeout=None, verify=True, cert=None, proxies=None): try: return super().send(request, stream, timeout, verify, cert, proxies) except requests.exceptions.RetryError as e: # 提取重试失败时的最后一次响应 if e.response is not None: # 直接触发HTTPError,和raise_for_status()行为一致 e.response.raise_for_status() # 如果没有响应(比如连接失败),重新抛出原异常 raise session = requests.Session() retry_config = Retry( total=3, status_forcelist=[500], allowed_methods=["GET"] ) adapter = RetryAwareAdapter(max_retries=retry_config) session.mount("http://", adapter) session.mount("https://", adapter) # 现在请求失败时会直接抛出HTTPError response = session.get("http://example.com/500")
内容的提问来源于stack exchange,提问作者Orili
相关产品推荐
相关产品推荐

