Python requests 重试机制

本文探讨了在使用requests库进行网络请求时遇到的超时问题,并介绍了如何通过设置DEFAULT_RETRIES参数来增加重试次数。深入解析了Retry机制的工作原理,包括其在DNS解析错误、链接错误及链接超时等情况下的应用,以及backoff算法在重试间隔中的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >



一、遇到问题

 response = requests.post(cgi, data=body, headers=http_headers, timeout=5, verify=False)

偶发报错:
… requests/adapters.py", line 449, in send
raise ReadTimeout(e, request=request)
ReadTimeout: HTTPSConnectionPool(host=‘weixin.oa.com’, port=443): Read timed out. (read timeout=5)



二、增加重试

try:
   requests.adapters.DEFAULT_RETRIES = 2
   response = requests.post(cgi, data=body, headers=http_headers, timeout=5, verify=False)
except Exception as e:
  LOGGER.error(e)


三、DEFAULT_RETRIES 解析

class HTTPAdapter(BaseAdapter):

    """The built-in HTTP Adapter for urllib3.
    :param max_retries: The maximum number of retries each connection
        should attempt. Note, 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. If you need granular control over the conditions under
        which we retry a request, import urllib3's ``Retry`` class and pass
        that instead.
    Usage::
      >>> import requests
      >>> s = requests.Session()
      >>> a = requests.adapters.HTTPAdapter(max_retries=3)
      >>> s.mount('http://', a)
    """
    
    __attrs__ = ['max_retries', 'config', '_pool_connections', '_pool_maxsize',
                 '_pool_block']
                 
    def __init__(self, pool_connections=DEFAULT_POOLSIZE,
                 pool_maxsize=DEFAULT_POOLSIZE, max_retries=DEFAULT_RETRIES,
                 pool_block=DEFAULT_POOLBLOCK):
                 
        if max_retries == DEFAULT_RETRIES:
            self.max_retries = Retry(0, read=False)
        else:
            self.max_retries = Retry.from_int(max_retries)

Retry设计:
在HTTPConnectionPool中,根据返回异常和方法,区分具体是哪种链接失败 (connect or read?),
然后减少对应的值,
接着再判断是否所有的操作重试都归零量,
归零后则报 MaxRetries 异常。

每次重试之间的间隔,使用了 backoff 算法。



四、 backoff 算法

class Retry(object):

    """ Retry configuration.
    Each retry attempt will create a new Retry object with updated values, so
    they can be safely reused.
    Retries can be defined as a default for a pool::
        retries = Retry(connect=5, read=2, redirect=5)
        http = PoolManager(retries=retries)
        response = http.request('GET', 'http://example.com/')
    Or per-request (which overrides the default for the pool)::
        response = http.request('GET', 'http://example.com/', retries=Retry(10))
    Retries can be disabled by passing ``False``::
        response = http.request('GET', 'http://example.com/', retries=False)
    Errors will be wrapped in :class:`~urllib3.exceptions.MaxRetryError` unless
    retries are disabled, in which case the causing exception will be raised.
    :param int total:
        Total number of retries to allow. Takes precedence over other counts.
        Set to ``None`` to remove this constraint and fall back on other
        counts. It's a good idea to set this to some sensibly-high value to
        account for unexpected edge cases and avoid infinite retry loops.
        Set to ``0`` to fail on the first retry.
        Set to ``False`` to disable and imply ``raise_on_redirect=False``.
    ....
    
    :param iterable method_whitelist:
        Set of uppercased HTTP method verbs that we should retry on.
        By default, we only retry on methods which are considered to be
        indempotent (multiple requests with the same parameters end with the
        same state). See :attr:`Retry.DEFAULT_METHOD_WHITELIST`.
    :param iterable status_forcelist:
        A set of HTTP status codes that we should force a retry on.
        By default, this is disabled with ``None``.
    :param float backoff_factor:
        A backoff factor to apply between attempts. urllib3 will sleep for::
            {backoff factor} * (2 ^ ({number of total retries} - 1))
        seconds. If the backoff_factor is 0.1, then :func:`.sleep` will sleep
        for [0.1s, 0.2s, 0.4s, ...] between retries. It will never be longer
        than :attr:`Retry.BACKOFF_MAX`.
        By default, backoff is disabled (set to 0).
        
    """
    
    DEFAULT_METHOD_WHITELIST = frozenset([
        'HEAD', 'GET', 'PUT', 'DELETE', 'OPTIONS', 'TRACE'])
        
    #: Maximum backoff time.
    BACKOFF_MAX = 120
    
    def __init__(self, total=10, connect=None, read=None, redirect=None,
                 method_whitelist=DEFAULT_METHOD_WHITELIST, status_forcelist=None,
                 backoff_factor=0, raise_on_redirect=True, raise_on_status=True,
                 _observed_errors=0):
        self.total = total
        self.connect = connect
        self.read = read
        if redirect is False or total is False:
            redirect = 0
            raise_on_redirect = False
        self.redirect = redirect
        self.status_forcelist = status_forcelist or set()
        self.method_whitelist = method_whitelist
        self.backoff_factor = backoff_factor
        self.raise_on_redirect = raise_on_redirect
        self.raise_on_status = raise_on_status
        self._observed_errors = _observed_errors  # TODO: use .history instead?
        
    def get_backoff_time(self):
        """ Formula for computing the current backoff
        :rtype: float
        """
        if self._observed_errors <= 1:
            return 0
        # 重试算法, _observed_erros就是第几次重试,每次失败这个值就+1.
        # backoff_factor = 0.1, 重试的间隔为[0.1, 0.2, 0.4, 0.8, ..., BACKOFF_MAX(120)]
        backoff_value = self.backoff_factor * (2 ** (self._observed_errors - 1))
        return min(self.BACKOFF_MAX, backoff_value)
        
    def sleep(self):
        """ Sleep between retry attempts using an exponential backoff.
        By default, the backoff factor is 0 and this method will return
        immediately.
        """
        backoff = self.get_backoff_time()
        if backoff <= 0:
            return
        time.sleep(backoff)
        
    def is_forced_retry(self, method, status_code):
        """ Is this method/status code retryable? (Based on method/codes whitelists)
        """
        if self.method_whitelist and method.upper() not in self.method_whitelist:
            return False
        return self.status_forcelist and status_code in self.status_forcelist
        
# For backwards compatibility (equivalent to pre-v1.9):
Retry.DEFAULT = Retry(3)

1、使用requests.get,默认Retry 3 次;

2、默认Retry时机:
DNS解析错误
链接错误
链接超时

3、读取超时、写超时、HTTP协议错误等默认不会Retry 。




参考文档 : requestsAPI文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值