python重试(指数退避算法)

时间:2022-05-02
本文章向大家介绍python重试(指数退避算法),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

本文实现了一个重试的装饰器,并且使用了指数退避算法。指数退避算法实现还是很简单的。先上代码再详细解释。

1、指数退避算法

欠奉。http://hugnew.com/?p=814

2、重试装饰器retry实现

# -*- coding:utf-8 -*-
import time
from random import randint
from struct import Result, ProcedureException


def retry(max_retries=3, max_wait_interval=10, period=1, rand=False):

    def _retry(func):

        def __retry(*args, **kwargs):
            MAX_RETRIES = max_retries
            MAX_WAIT_INTERVAL = max_wait_interval
            PERIOD = period
            RAND = rand

            retries = 0
            error = None
            while retries < MAX_RETRIES:
                try:
                    result = func(*args, **kwargs)
                    if result.code == Result.ERROR:
                        raise ProcedureException("procedure occur error")
                    if result.code == Result.TIMEOUT:
                        raise ProcedureException("procedure request time out")
                    if result.code == Result.SUCCESS:
                        return result
                except Exception, ex:
                    error = ex
                finally:
                    sleep_time = min(2 ** retries * PERIOD if not RAND else randint(0, 2 ** retries) * PERIOD, MAX_WAIT_INTERVAL)
                    time.sleep(sleep_time)
                    retries += 1
                    print "第", retries, "次重试, ", "等待" , sleep_time, "秒"
            if retries == MAX_RETRIES:
                if error:
                    raise error
                else:
                    raise ProcedureException("unknown")
        return __retry
    return _retry

这里我们自己定义了两个东西:

1)枚举类Result,标识过程调用的状态,其中有三个状态,成功SUCCESS,失败ERROR,超时TIMEOUT;

2)异常ProcedureException,在retry装饰器中我们判断了状态,如果是失败和超时场景,我们将会抛出这个异常。

这两个东西的实现如下

from enum import Enum, unique

@unique
class Result(Enum):
    SUCCESS = 0
    TIMEOUT = 1
    ERROR = 2


class ProcedureException(Exception):
    def __init__(self, message):
        Exception.__init__(self, message)

retry装饰器会重试以下两个场景:

1)Procedure函数func出现异常:TIMEOUT和ERROR

2)未知异常:Procedure函数func可以抛出未能处理的异常,例如func函数可能是网络读写,遇到网络超时,链接断开等,抛出timeout或者broken pipe。

是否随机:

1)不随机,将会以2**retries,作为重试时间

2)随机,将会在(0,2**retries)之间随机一个数,作为重试时间

其实指数退避算法就是使用随机“抖动”的方式来解决高并发场景下信道碰撞的,但是我们的应用场景也有需要持续增加重试间隔(而不是增加几率)的情况。

3、测试一下

我们测试两个场景,重试10次和随机,重试5次不随机。

1)重试10次,随机,最大间隔10s

# -*- coding:utf-8 -*-
from decorator import retry
from struct import Result

@retry(rand=True, max_retries=10, max_wait_interval=10)
def do_something():

    class result(object):
        def __init__(self, code):
            self.code = code
    print "##########  调用结果", Result.ERROR, " ############"
    return result(Result.ERROR)

do_something()

输出结果

/Users/didi/anaconda/bin/python /Users/didi/test/pythoneer/retry/test.py
##########  调用结果 Result.ERROR  ############
第 1 次重试,  等待 0 秒
##########  调用结果 Result.ERROR  ############
第 2 次重试,  等待 1 秒
##########  调用结果 Result.ERROR  ############
第 3 次重试,  等待 2 秒
##########  调用结果 Result.ERROR  ############
第 4 次重试,  等待 0 秒
##########  调用结果 Result.ERROR  ############
第 5 次重试,  等待 10 秒
##########  调用结果 Result.ERROR  ############
第 6 次重试,  等待 10 秒
##########  调用结果 Result.ERROR  ############
第 7 次重试,  等待 10 秒
##########  调用结果 Result.ERROR  ############
第 8 次重试,  等待 10 秒
##########  调用结果 Result.ERROR  ############
第 9 次重试,  等待 10 秒
##########  调用结果 Result.ERROR  ############
Traceback (most recent call last):
第 10 次重试,  等待 10 秒
  File "/Users/didi/test/pythoneer/retry/test.py", line 14, in <module>
    do_something()
  File "/Users/didi/test/pythoneer/retry/decorator.py", line 36, in __retry
    if error:
struct.ProcedureException: procedure occur error

2)重试5次,不随机,最大间隔10s

# -*- coding:utf-8 -*-
from decorator import retry
from struct import Result

@retry(rand=False, max_retries=5, max_wait_interval=10)
def do_something():

    class result(object):
        def __init__(self, code):
            self.code = code
    print "##########  调用结果", Result.ERROR, " ############"
    return result(Result.ERROR)

do_something()

输出结果

/Users/didi/anaconda/bin/python /Users/didi/test/pythoneer/retry/test.py
##########  调用结果 Result.ERROR  ############
第 1 次重试,  等待 1 秒
##########  调用结果 Result.ERROR  ############
第 2 次重试,  等待 2 秒
##########  调用结果 Result.ERROR  ############
第 3 次重试,  等待 4 秒
##########  调用结果 Result.ERROR  ############
第 4 次重试,  等待 8 秒
##########  调用结果 Result.ERROR  ############
第 5 次重试,  等待 10 秒
Traceback (most recent call last):
  File "/Users/didi/test/pythoneer/retry/test.py", line 14, in <module>
    do_something()
  File "/Users/didi/test/pythoneer/retry/decorator.py", line 37, in __retry
    raise error
struct.ProcedureException: procedure occur error