zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    Python生成二维码
    SSO单点登录
    小说 · 凉生,我们可不可以不忧伤
    RabbitMQ入门教程——.NET客户端使用
    ASP.NET MVC 拓展ActionResult实现Html To Pdf 导出
    ASP.NET MVC 拓展ViewResult实现word文档下载
    RabbitMQ入门教程——安装及配置
    MongoDB学习笔记——分片(Sharding)
    MongoDB学习笔记——Replica Set副本集
    MongoDB学习笔记——Master/Slave主从复制
  • 原文地址:https://www.cnblogs.com/kangoroo/p/7601338.html
Copyright © 2011-2022 走看看