【问题标题】:Pythonic way of retry running a function [duplicate]重试运行函数的Pythonic方式[重复]
【发布时间】:2014-02-14 18:02:01
【问题描述】:

Python 专业人士如何重试运行请求网络服务的函数(网络服务有时会失败)

功能:

def some_request(uri):
    try:
        req = requests.post('http://someuri.com', {'test': 'yes'})
    except Exception as e:
        return False
    return {'id': req.json()['value'], 'other': req.json()['other']}

您使用 while 或其他 python 习语来处理重试?

请告诉我如何以正确的方式进行操作。

【问题讨论】:

  • 增加requests库的重试次数并让它处理问题。
  • 而且你很少需要抓毯子Exception; requests.exceptions 包含所有可以抛出的特定异常,请从那里选择一个。

标签: python exception


【解决方案1】:

定义重试实用程序:

# Retry utility   
# set logging for `retry` channel
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('retry')

# Define Exception class for retry
class RetryException(Exception):
    u_str = "Exception ({}) raised after {} tries."

    def __init__(self, exp, max_retry):
        self.exp = exp
        self.max_retry = max_retry    
    def __unicode__(self):
        return self.u_str.format(self.exp, self.max_retry)
    def __str__(self):
        return self.__unicode__()

# Define retry util function
def retry_func(func, max_retry=10):
    """
    @param func: The function that needs to be retry
    @param max_retry: Maximum retry of `func` function, default is `10`
    @return: func
    @raise: RetryException if retries exceeded than max_retry
    """
    for retry in range(1, max_retry + 1):
        try:
            return func()
        except Exception, e:
            logger.info('Failed to call {}, in retry({}/{})'.format(func.func,
                                                           retry, max_retry))
    else:
        raise RetryException(e, max_retry)

使用它:

from functools import partial
import requests

def some_request(uri, post):
    req = requests.post(uri, post)
    return req

uri = 'http://someuri.com'
post = {'test': 'yes'}

try:
    retry_func(partial(some_request, uri, post), max_retry=3)
except RetryException, e:
    print(e)

输出:

INFO:requests.packages.urllib3.connectionpool:Starting new HTTP connection (1): someuri.com
INFO:retry:Failed to call <function some_request at 0x7faaba2318c0>, in retry(1/3)
INFO:requests.packages.urllib3.connectionpool:Starting new HTTP connection (1): someuri.com
INFO:retry:Failed to call <function some_request at 0x7faaba2318c0>, in retry(2/3)
INFO:requests.packages.urllib3.connectionpool:Starting new HTTP connection (1): someuri.com
INFO:retry:Failed to call <function some_request at 0x7faaba2318c0>, in retry(3/3)
Exception (HTTPConnectionPool(host='someuri.com', port=80): Max retries exceeded with url: / (Caused by <class 'socket.gaierror'>: [Errno -2] Name or service not known)) raised after 3 tries.

【讨论】:

  • 可能有点矫枉过正,但这绝对是最好和最完整的答案。考虑注释您的代码以提高可读性,这应该是一个明显的复选标记
  • @adsmith,谢谢,我更新了我的答案。
【解决方案2】:

我使用的模式重试次数有限,每次尝试之间的睡眠时间间隔很短,呈指数增长,最后在反复失败后引发最后一次看到的异常:

def wrappedRequest( self, uri, args ):
    lastException = None 
    interval = 1.0
    for _ in range(3):
        try:
            result = requests.post(uri, args)
            return result
        except Exception as e:
            lastException = e

        time.sleep(interval)
        interval *= 2.0
    raise lastException 

(我实际上专门检查 HTTPError、URLError、IOError 和 BadStatusLine 异常,而不是广泛的异常网络。)

【讨论】:

  • 我喜欢你的回答,但你的代码有问题:1)如果第二次尝试成功,它会从第一次失败的尝试中引发异常; 2)如果第一次尝试成功,它将raise None,这是Python中的错误; 3)它会在最后一次尝试时无用地休眠
  • 不是这样。如果任何尝试成功,它会从 try 块内部返回,同时跳过 raisesleep
【解决方案3】:

这肯定需要装饰器。

你可能想要这样的东西:

import functools
def retry(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        retExc = None
        for i in xrange(4):
            try:
                return func(*args, **kwargs)
            except Exception, e:
                retExc = e
        raise retExc
    return wrapper

然后运行您的代码,只需使用:

@retry
def some_request(...):
    ...

你可以通过添加一点睡眠、添加一个 numTimes 来重试等来让它变得更漂亮,但这样就可以了。

【讨论】:

    【解决方案4】:

    这是一个通用装饰器,可让您指定重试次数以及要忽略的异常类型:

    from functools import wraps
    def retry(count=5, exc_type=Exception):
        def decorator(func):
            @wraps(func):
            def result(*args, **kwargs):
                for _ in range(count):
                    try:
                        return func(*args, **kwargs)
                    except exc_type:
                        pass
                    raise
             return result
         return decorator
    

    像这样使用它:

    @retry(count=10, exc_type=IOError)
    def read_file():
        pass # do something
    

    【讨论】:

      猜你喜欢
      • 2013-12-30
      • 1970-01-01
      • 2022-01-23
      • 2012-09-29
      • 1970-01-01
      • 1970-01-01
      • 2013-05-31
      • 2015-04-21
      • 2020-12-08
      相关资源
      最近更新 更多