每日一包 - retry

300 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情

介绍

我们在开发程序时,尤其是涉及到网络请求的情况下,比如爬虫的程序,会高频率的涉及到网络请求,但是常常会出现因为各种原因而请求失败的情况,就需要不断的重试请求,这种情况下,就可以用到retry 来帮助我们优雅的进行重试操作了。

安装和使用

安装

pip install retry

使用

retry

retry 本质上就是一个装饰器,通过装饰器的不同参数来控制重试某一段函数的执行方式,因此我们首先需要知道retry 都有哪些参数,以及这些参数表示的意思。

def retry(exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1, jitter=0, logger=logging_logger):
    """Return a retry decorator.
​
    :param exceptions: 一个异常类型,或者是能够捕获到的异常类型的元组,默认是Exception.
    :param tries: 最大尝试次数,默认是-1表示重试无限次。
    :param delay: 每次重试之间的时间间隔,默认是0即没有时间间隔
    :param max_delay: 每次重试之间最大的时间间隔,默认值无,即没有限制
    :param backoff: 下次重试是上次重试时间间隔的多少倍,默认是1,即每次重试时间间隔相同
    :param jitter: 每次重试时间间隔增加多少时长,默认是0, 如果提供的参数是一个元组,那么增加的时长就是元组中的随机数。
    :param logger:  在尝试失败时调用retry的日志模块,如果为None,则不记录
    """

下面我们就用代码示例进行简单演示:

from retry import retry
@retry()
def make_trouble():
    """
    Retry until succeed
    :return:
    """
    print("重试直到代码成功")
@retry(ZeroDivisionError, tries=3, delay=2)
def make_trouble():
    '''当出现ZeroDivisionError异常时,进行3次重试,每次充实之间的时间间隔是2s'''
@retry((ValueError, TypeError), delay=1, backoff=2)
def make_trouble():
    '''当出现ValueError or TypeError时,每次重试时间间隔都是上一次重试时间间隔的2倍'''
@retry((ValueError, TypeError), delay=1, backoff=2, max_delay=4)
def make_trouble():
    '''当出现ValueError or TypeError时,每次重试时间间隔都是上一次重试时间间隔的2倍,但是由于限制了最大时间间隔的参数,因此时间间隔变为1 2 4 4 ....'''
@retry(ValueError, delay=1, jitter=1)
def make_trouble():
    '''当出现ValueError时, 每次重试的时间间隔都是上一次重试时间间隔+1,即1 2 3 4....'''

当然上面的代码默认我们都开启了日式记录,如果需要查看具体日志,我们可以通过下面的方式进行日志查看:

if __name__ == '__main__':
    import logging
    logging.basicConfig()  # 将日志打印到终端
    make_trouble()

retry_call

除了可以使用retry装饰器之外,如果函数执行失败并且需要重复执行的时候,我们也可以使用retry_call,该方法和装饰器非常类似,但是该方法可以接受函数和函数参数作为该方法的参数,可以动态的调整函数重试时的参数,比retry更加灵活。

首先来看一下该方法的详细使用方式:

def retry_call(f, fargs=None, fkwargs=None, exceptions=Exception, tries=-1, delay=0, max_delay=None, backoff=1,
               jitter=0,
               logger=logging_logger):
    """
    当函数执行失败时重新调用函数.
​
    :param f: 需要被执行的函数.
    :param fargs: 执行函数的位置参数.
    :param fkwargs: 执行函数的关键字参数.
    :param exceptions: 异常类型,可以是一个类型也可以是一个元组.
    :其他参数参考retry装饰器
    :returns: 执行函数的返回值.
    """

知道了该方法的参数,就简单的看一下代码示例:

import requests
​
from retry.api import retry_call
​
​
def make_trouble(service, info=None):
    if not info:
        info = ''
    r = requests.get(service + info)
    return r.text
​
​
def what_is_my_ip(approach=None):
    if approach == "optimistic":
        tries = 1
    elif approach == "conservative":
        tries = 3
    else:
        # skeptical
        tries = -1
    result = retry_call(make_trouble, fargs=["http://ipinfo.io/"], fkwargs={"info": "ip"}, tries=tries)
    print(result)
​
what_is_my_ip("conservative")

总结

当我们清楚的知道我们的代码可能会出现未知的异常时,为了更加方便的执行代码灵活的使用retry 模块会使开发效率更高,至于使用retry 还是 retry_call 就根据实际代码情况选择即可。