你不知道的Requests进阶技巧:用Retry实现完美重试
你不知道的Requests进阶技巧:用Retry实现完美重试
在使用Python的Requests库进行HTTP请求时,遇到网络不稳定或服务器响应异常的情况,合理的重试机制可以提高请求的成功率。本文将详细介绍Requests库中Retry类的使用方法,帮助开发者实现更灵活和精细的重试策略。
在上一篇文章中,我们使用了HTTPAdapter中的max_retries参数对超时类的原因进行了重试。本篇文章中,我们将使用Retry类对重试的原因和过程进行精细化控制。
import logging
import requests
from requests.adapters import HTTPAdapter
# 开启 urllib3 的日志,方便查看重试过程
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
urllib3_logger = logging.getLogger('urllib3')
urllib3_logger.setLevel(logging.DEBUG)
http_adapter = HTTPAdapter(max_retries=3)
# 使用 session 发送请求
session = requests.session()
# 打印 adapters
print(session.adapters)
session.mount('https://', http_adapter)
session.mount('http://', http_adapter)
try:
print(session.get('https://www.baidu.com', timeout=0.01).text[:100])
except Exception as e:
print(e)
print(type(e))
以上为之前的代码进行了一些小修改,在初始化HTTPAdapter时,传入了max_retries参数,意思是在网络超时的时候重试三次。那如果不超时只是服务器返回了不正常的响应,应该如何进行更加方便的重试呢?
查看HTTPAdapter类的初始化代码可以发现,max_retries参数最终会被赋值为Retry的实例,并且将我们的参数传入。那么如果我们想控制重试的时机以及过程,只需要传入一个自定义的Retry类就好了。
来看一下Retry的文档
Retry类参数详解
- total
- 功能:控制总的重试次数。如果设置为
None
,则禁用总重试次数。 - 默认值:
10
- 说明:这是总的重试次数,无论错误原因是什么。包括连接失败、超时等。
- connect
- 功能:针对连接错误(如DNS解析错误、TCP连接失败等)重试的次数。
- 默认值:
None
- 说明:如果为
None
,则使用total
的值。
- read
- 功能:控制在读取数据时(比如在连接成功后但在获取响应时出错)重试的次数。
- 默认值:
None
- 说明:如果为
None
,则使用total
的值。
- redirect
- 功能:重定向时允许的最大次数。
- 默认值:
None
- 说明:如果为
None
,默认使用total
的值。设置为0禁止重定向。
- status
- 功能:针对特定HTTP响应状态码(例如500、502、503、504)重试的次数。
- 默认值:
None
- 说明:如果为
None
,使用total
的值。
- method_whitelist
- 功能:一个允许重试的HTTP方法的列表。
- 默认值:
frozenset(['HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE'])
- 说明:如果HTTP方法不在此列表中,则不进行重试。
- status_forcelist
- 功能:定义需要强制重试的HTTP状态码列表。
- 默认值:
None
- 说明:如果服务器返回此列表中的状态码,则强制重试。
- backoff_factor
- 功能:控制重试之间的等待时间,等待时间的增长因子(用于指数回退)。
- 默认值:
0
- 说明:重试间隔时间为
backoff_factor * (2 ** (retry次数 - 1))
。
- raise_on_redirect
- 功能:如果设置为
True
,在重定向时抛出MaxRetryError
。 - 默认值:
True
- 说明:用于控制是否在重定向次数超过限制时抛出异常。
- raise_on_status
- 功能:如果设置为
True
,当重试次数超过最大值且遇到特定状态码时抛出异常。 - 默认值:
False
- 说明:当遇到
status_forcelist
中的状态码并超出重试次数时,抛出异常。
- history
- 功能:保存历史重试请求的列表,包含过去的所有重试信息。
- 默认值:
None
- 说明:可以跟踪重试的历史记录。
- respect_retry_after_header
- 功能:是否尊重服务器返回的
Retry-After
头信息。 - 默认值:
True
- 说明:如果服务器在响应中返回
Retry-After
头部,设置True
时将等待指定的时间后再进行重试。
- remove_headers_on_redirect
- 功能:在重定向时移除的HTTP头信息列表。
- 默认值:
frozenset(['Authorization'])
- 说明:重定向时可以选择移除敏感的头部信息,如
Authorization
。
- method_whitelist / allowed_methods(新的版本中使用
allowed_methods
)
- 功能:允许重试的HTTP方法。
- 默认值:
frozenset(['HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE'])
- 说明:控制哪些HTTP请求方法允许进行重试。
- retry_on_exception
- 功能:自定义逻辑,指定哪些异常可以触发重试。
- 默认值:
None
- 说明:接受一个回调函数,返回
True
则重试。
- retry_on_status
- 功能:自定义逻辑,指定哪些HTTP状态码可以触发重试。
- 默认值:
None
- 说明:接受一个回调函数,返回
True
则重试。
实战示例
以下示例演示了如何应用自定义的重试策略来处理特定的HTTP状态码。
import logging
import requests
from requests.adapters import HTTPAdapter
from urllib3 import Retry
# 开启 urllib3 的日志,方便查看重试过程
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
urllib3_logger = logging.getLogger('urllib3')
urllib3_logger.setLevel(logging.DEBUG)
retry_strategy = Retry(
total=5, # 总共重试 5 次
connect=3, # 连接错误时重试 3 次
read=2, # 读取错误时重试 2 次
redirect=3, # 重定向时最多重试 3 次
status_forcelist=[500, 502, 503, 504], # 对于这些 HTTP 状态码强制重试
backoff_factor=1, # 等待时间因子:指数递增,如 1s, 2s, 4s...
raise_on_status=False # 达到最大重试次数后不抛异常
)
http_adapter = HTTPAdapter(max_retries=retry_strategy)
# 使用 session 发送请求
session = requests.session()
# 打印 adapters
print(session.adapters)
session.mount('https://', http_adapter)
session.mount('http://', http_adapter)
# 发送请求
try:
response = session.get("https://httpbin.org/status/500") # 故意发送到一个会返回 500 错误的 URL
print(response.status_code)
except requests.exceptions.RetryError as e:
print(f"达到最大重试次数,重试失败: {e}")
except requests.exceptions.RequestException as e:
print(f"请求失败: {e}")
在上面的代码中,定义了一个自定义的Retry类,当我们访问https://httpbin.org/status/500
(它返回500错误状态码),请求会自动按照指定策略进行重试,并打印重试过程中的日志,运行结果为:
查看运行结果,总共请求了6次,其中5次是由于状态码500进行的重试,每次重试的间隔时间按设定的因子指数增长。并且每次重试后,总重试次数都在减少,最终减少到0。
大家可以按照自己的需求来初始化Retry类,可以更加方便的进行重试。如果以上参数都不能满足要求,可以使用retry_on_status
或retry_on_exception
在发生指定异常或者指定状态码时执行自定义函数,可以更加灵活的实现重试逻辑。
在实际开发中,利用Requests库的Retry类可以灵活应对非网络问题的重试需求。大家可以根据项目的具体情况,自定义重试参数。
retry_strategy = Retry(
# 自行定义参数,默认不重试
)
http_adapter = HTTPAdapter(max_retries=retry_strategy)