我可以设置max_retries for requests.request吗?

Python请求模块简单而优雅,但有一件事让我很恼火。 可以使用类似于

的消息来获得requests.exception.ConnectionError
Max retries exceeded with url: ...

这意味着请求可以多次尝试访问数据。但是在任何文件中都没有提到这种可能性。查看源代码,我没有找到任何可以更改默认值(假设为0)的地方。

那么是否有可能设置请求的最大重试次数呢?

259334 次浏览

执行重试的是底层的urllib3库。要设置不同的最大重试计数,使用可供选择的传输适配器:

from requests.adapters import HTTPAdapter


s = requests.Session()
s.mount('http://stackoverflow.com', HTTPAdapter(max_retries=5))

max_retries参数接受一个整数或Retry()对象;后者让你细粒度地控制重试的失败类型(一个整数值被转换为Retry()实例,它只处理连接失败;默认情况下,连接后的错误不会被处理,因为这些错误可能会导致副作用)。


旧的答案,在请求1.2.1发布之前:

requests库并没有真正使其可配置,也不打算这样做(参见这个拉请求)。目前(请求1.1),重试计数设置为0。如果你真的想设置一个更高的值,你必须全局设置这个:

import requests


requests.adapters.DEFAULT_RETRIES = 5

这个常数没有记录;使用它的风险由您自己承担,因为未来的版本可能会改变这种处理方式。

更新:和这个做了变化;在1.2.1版本中,设置max_retries参数的选项HTTPAdapter()上被添加,所以现在你必须使用替代传输适配器,见上文。猴子补丁方法不再有效,除非你也修补HTTPAdapter.__init__()默认值(非常不推荐)。

注意,Martijn Pieters的答案不适合1.2.1+版本。如果不给库打补丁,就不能全局设置它。

你可以这样做:

import requests
from requests.adapters import HTTPAdapter


s = requests.Session()
s.mount('http://www.github.com', HTTPAdapter(max_retries=5))
s.mount('https://www.github.com', HTTPAdapter(max_retries=5))

这不仅会改变max_retries,而且还会启用一个回退策略,该策略使对所有http://地址的请求在重试之前休眠一段时间(总共5次):

import requests


from requests.adapters import HTTPAdapter, Retry


s = requests.Session()


retries = Retry(total=5,
backoff_factor=0.1,
status_forcelist=[ 500, 502, 503, 504 ])


s.mount('http://', HTTPAdapter(max_retries=retries))


s.get('http://httpstat.us/500')

根据Retry的文档:如果backoff_factor是0.1,那么sleep()将休眠[0.05s, 0.1s, 0.2s, 0.4s,…])。如果返回的状态代码是500502503504,它也将强制重试。

Retry的各种其他选项允许更细粒度的控制:

  • 总计 -允许重试的总数。
  • 连接 -有多少连接相关的错误要重试。
  • -读取错误时重试多少次。
  • 重定向 -执行多少重定向。
  • method_whitelist -我们应该重试的大写HTTP方法谓词集。
  • status_forcelist -我们应该强制重试的一组HTTP状态码。
  • backoff_factor -在两次尝试之间应用的回退因子。
  • raise_on_redirect -如果重定向的数量已耗尽,是否引发MaxRetryError,或返回带有3 xx范围内响应代码的响应。
  • raise_on_status -含义类似于raise_on_redirect:如果状态落在status_forcelist范围内,并且重试已经耗尽,我们是否应该引发异常,还是返回响应。

NB: raise_on_status相对较新,还没有成为urllib3或请求的发布。 raise_on_status关键字参数似乎最多在python版本3.6中被纳入标准库。

要使请求重试特定的HTTP状态码,使用status_forcelist。例如,status_forcelist = [503]将重试状态代码503(服务不可用)。

默认情况下,重试仅在以下条件下触发:

  • 无法从池中获得连接。
  • TimeoutError
  • HTTPException被抛出(从Python 3中的http.client else httplib)。 这似乎是低级HTTP异常,如URL或protocol not 李形成正确。< / >
  • SocketError
  • ProtocolError

注意,这些都是阻止接收常规HTTP响应的异常。如果生成了任何常规响应,则不进行重试。如果不使用status_forcelist,即使状态为500的响应也不会被重试。

为了使它以一种更直观的方式与远程API或web服务器一起工作,我将使用上面的代码片段,它强制重试状态500502503504,所有这些在web上并不罕见,并且(可能)在一个足够大的回退周期内可以恢复。

获得更高控制的一种更干净的方法可能是将重试内容打包到函数中,并使用装饰器使该函数可检索,并将异常白名单。

我在这里创建了相同的: http://www.praddy.in/retry-decorator-whitelisted-exceptions/ < / p >

在该链接中重现代码:

def retry(exceptions, delay=0, times=2):
"""
A decorator for retrying a function call with a specified delay in case of a set of exceptions


Parameter List
-------------
:param exceptions:  A tuple of all exceptions that need to be caught for retry
e.g. retry(exception_list = (Timeout, Readtimeout))
:param delay: Amount of delay (seconds) needed between successive retries.
:param times: no of times the function should be retried




"""
def outer_wrapper(function):
@functools.wraps(function)
def inner_wrapper(*args, **kwargs):
final_excep = None
for counter in xrange(times):
if counter > 0:
time.sleep(delay)
final_excep = None
try:
value = function(*args, **kwargs)
return value
except (exceptions) as e:
final_excep = e
pass #or log it


if final_excep is not None:
raise final_excep
return inner_wrapper


return outer_wrapper


@retry(exceptions=(TimeoutError, ConnectTimeoutError), delay=0, times=3)
def call_api():

在苦苦思索了一些答案之后,我找到了一个名为倒扣的库,它更适合我的情况。一个基本的例子:

import backoff


@backoff.on_exception(
backoff.expo,
requests.exceptions.RequestException,
max_tries=5,
giveup=lambda e: e.response is not None and e.response.status_code < 500
)
def publish(self, data):
r = requests.post(url, timeout=10, json=data)
r.raise_for_status()

我仍然建议尝试一下库的原生功能,但如果遇到任何问题或需要更广泛的控制,后退也是一种选择。

你可以使用请求库一次性完成所有任务。 如果您收到429,500,502,503或504状态码,则以下代码将重试3次,每次都通过“backoff_factor”设置较长的延迟。

.

.
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry


retry_strategy = Retry(
total=3,
backoff_factor=1,
status_forcelist=[429, 500, 502, 503, 504],
method_whitelist=["HEAD", "GET", "OPTIONS"]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
http = requests.Session()
http.mount("https://", adapter)
http.mount("http://", adapter)


response = http.get("https://en.wikipedia.org/w/api.php")