【问题标题】:Can I set max_retries for requests.request?我可以为 requests.request 设置 max_retries 吗?
【发布时间】:2013-03-15 11:19:02
【问题描述】:

Python requests 模块简单而优雅,但有一件事让我很头疼。 有可能得到一个 requests.exception.ConnectionError 并带有如下消息:

Max retries exceeded with url: ...

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

那么是否有可能以某种方式设置请求的最大重试次数?

【问题讨论】:

  • 关于 2.x 请求的任何更新?希望有一个 requests.get(url, max_retries=num_max_retries)) 实现。
  • @paragbaxi: 更好的是requests.get(url, max_retries=num_max_retries, dely_between_retries=3))
  • 关于请求重试的有用文章:peterbe.com/plog/best-practice-with-retries-with-requests

标签: python python-requests


【解决方案1】:

这不仅会更改 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')

根据documentation for Retry:如果 backoff_factor 为 0.1,则 sleep() 将在重试之间休眠 [0.05s, 0.1s, 0.2s, 0.4s, ...]。如果返回的状态码是500502503504,它也会强制重试。 p>

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

  • total – 允许重试的总次数。
  • connect – 要重试多少个与连接相关的错误。
  • read – 读取错误重试多少次。
  • redirect – 执行多少次重定向。
  • method_whitelist – 我们应该重试的大写 HTTP 方法动词集。
  • status_forcelist – 我们应该强制重试的一组 HTTP 状态代码。
  • backoff_factor – 在尝试之间应用的退避因子。
  • raise_on_redirect – 如果重定向次数用尽,是否引发MaxRetryError,或返回响应代码在 3xx 范围内的响应。
  • raise_on_status – 与 raise_on_redirect 类似的含义:如果状态在 status_forcelist 范围内并且重试次数已用尽,我们是否应该引发异常或返回响应。李>

NBraise_on_status 相对较新,尚未发布 urllib3 或 requests。 raise_on_status 关键字参数似乎最多在 python 3.6 版中进入了标准库。

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

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

  • 无法从池中获得连接。
  • TimeoutError
  • HTTPException 引发(来自 Python 3 中的 http.client 否则 httplib)。 这似乎是低级 HTTP 异常,例如 URL 或协议不是 格式正确。
  • SocketError
  • ProtocolError

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

为了让它以更直观的方式与远程 API 或 Web 服务器一起工作,我会使用上面的代码 sn-p,它会强制重试状态 500502503504,所有这些在网络上并不少见,并且(可能)在足够大的回退期的情况下可以恢复。

【讨论】:

  • 我正在尝试实现您的逻辑,但我不知道它是否有效,因为即使 res 状态为 503,日志也只显示一个请求。我如何知道重试是否有效?见代码:pastebin.com/rty4bKTw
  • 附加代码按预期工作。诀窍是 status_forcelist 参数。这告诉 urllib3 包重试特定的状态代码。代码:pastebin.com/k2bFbH7Z
  • urllib3 不(也不应该)认为状态 503 是一个例外(默认情况下)。
  • @Connor 不,适配器已附加到会话。
  • urlib3.Retry 不再是请求的一部分。这必须直接导入。建议修改
【解决方案2】:

重试是底层的urllib3 库。要设置不同的最大重试次数,请使用alternative transport adapters

from requests.adapters import HTTPAdapter

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

max_retries 参数采用整数或Retry() object;后者使您可以对重试哪种故障进行细粒度控制(整数值转换为仅处理连接故障的Retry() 实例;默认情况下不处理建立连接后的错误,因为这些可能导致侧面-效果)。


旧答案,早于请求 1.2.1 的发布

requests 库并没有真正做到这一点,它也不打算这样做(参见this pull request)。目前(请求 1.1),重试次数设置为 0。如果您真的想将其设置为更高的值,则必须全局设置:

import requests

requests.adapters.DEFAULT_RETRIES = 5

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

更新:这个确实改变了;在版本 1.2.1 the option to set the max_retries parameter 上添加了 HTTPAdapter() class,因此现在您必须使用替代传输适配器,见上文。猴子补丁方法不再有效,除非您也修补 HTTPAdapter.__init__() 默认值(非常不推荐)。

【讨论】:

  • 如果不需要,您不必为每个站点都指定它。您可以只做session.mount('http://', HTTPAdapter(max_retries=10)) 这将适用于所有http 连接。与 https 相同的操作将适用于所有 https 连接。
  • @user136036:是的,适配器是通过最长前缀匹配来查找的;如果您希望这适用于 所有 url,那么 http://https:// 是要使用的最小前缀,请参阅答案链接到的文档。
  • 请注意,HTTPAdapter(max_retries=5) 仅适用于特定场景。来自requests docNote, this applies only to failed DNS lookups, socket connections and connection timeouts, never to requests where data has made it to the server. By default, Requests does not retry failed connections. 要强制重试任何状态代码,请参阅下面的@datashaman 答案。
  • @StevenXu:是的,你可以配置Retry()来改变重试的失败场景。
【解决方案3】:

小心,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))

【讨论】:

  • 不错的解决方案,但请注意重试之间没有延迟。如果您想在两次尝试之间睡觉,则需要自己滚动。
  • 你也可以添加超时 findwork.dev/blog/… 看到这个
  • @DJ_Stuffy_K 好文章!
【解决方案4】:

在这里找到一些答案后,我找到了一个名为backoff 的库,它更适合我的情况。一个基本的例子:

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()

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

【讨论】:

  • 很棒的图书馆,谢谢!除了requests 之外,我还需要这个功能,所以这非常有效!
【解决方案5】:

获得更高控制权的更简洁的方法可能是将重试内容打包到一个函数中,并使用装饰器使该函数可重试,并将异常列入白名单。

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

复制该链接中的代码:

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():

【讨论】:

  • 这是用于 python 2.0 的吗?
【解决方案6】:

您可以使用 requests 库一次性完成所有工作。 如果您收到 429,500,502,503 或 504 状态码,以下代码将重试 3 次,每次都通过“backoff_factor”设置更长的延迟。请参阅 https://findwork.dev/blog/advanced-usage-python-requests-timeouts-retries-hooks/ 获取不错的教程。

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")

【讨论】:

    猜你喜欢
    • 2012-05-29
    • 2011-02-05
    • 2011-04-26
    • 2014-01-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-08
    • 2013-12-04
    相关资源
    最近更新 更多