上一篇中我们使用了 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 的文档
1. total
- 功能:控制总的重试次数。如果设置为
None
,则禁用总重试次数。 - 默认值:
10
- 说明:这是总的重试次数,无论错误原因是什么。包括连接失败、超时等。
2. connect
- 功能:针对连接错误(如 DNS 解析错误、TCP 连接失败等)重试的次数。
- 默认值:
None
- 说明:如果为
None
,则使用total
的值。
3. read
- 功能:控制在读取数据时(比如在连接成功后但在获取响应时出错)重试的次数。
- 默认值:
None
- 说明:如果为
None
,则使用total
的值。
4. redirect
- 功能:重定向时允许的最大次数。
- 默认值:
None
- 说明:如果为
None
,默认使用total
的值。设置为 0 禁止重定向。
5. status
- 功能:针对特定 HTTP 响应状态码(例如 500、502、503、504)重试的次数。
- 默认值:
None
- 说明:如果为
None
,使用total
的值。
6. method_whitelist
- 功能:一个允许重试的 HTTP 方法的列表。
- 默认值:
frozenset(['HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE'])
- 说明:如果 HTTP 方法不在此列表中,则不进行重试。
7. status_forcelist
- 功能:定义需要强制重试的 HTTP 状态码列表。
- 默认值:
None
- 说明:如果服务器返回此列表中的状态码,则强制重试。
8. backoff_factor
- 功能:控制重试之间的等待时间,等待时间的增长因子(用于指数回退)。
- 默认值:
0
- 说明:重试间隔时间为
backoff_factor * (2 ** (retry次数 - 1))
。
9. raise_on_redirect
- 功能:如果设置为
True
,在重定向时抛出MaxRetryError
。 - 默认值:
True
- 说明:用于控制是否在重定向次数超过限制时抛出异常。
10. raise_on_status
- 功能:如果设置为
True
,当重试次数超过最大值且遇到特定状态码时抛出异常。 - 默认值:
False
- 说明:当遇到
status_forcelist
中的状态码并超出重试次数时,抛出异常。
11. history
- 功能:保存历史重试请求的列表,包含过去的所有重试信息。
- 默认值:
None
- 说明:可以跟踪重试的历史记录。
12. respect_retry_after_header
- 功能:是否尊重服务器返回的
Retry-After
头信息。 - 默认值:
True
- 说明:如果服务器在响应中返回
Retry-After
头部,设置True
时将等待指定的时间后再进行重试。
13. remove_headers_on_redirect
- 功能:在重定向时移除的 HTTP 头信息列表。
- 默认值:
frozenset(['Authorization'])
- 说明:重定向时可以选择移除敏感的头部信息,如
Authorization
。
14. method_whitelist
/ allowed_methods
(新的版本中使用 allowed_methods
)
- 功能:允许重试的 HTTP 方法。
- 默认值:
frozenset(['HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE'])
- 说明:控制哪些 HTTP 请求方法允许进行重试。
15. retry_on_exception
- 功能:自定义逻辑,指定哪些异常可以触发重试。
- 默认值:
None
- 说明:接受一个回调函数,返回
True
则重试。
16. 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)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。