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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏编程

说说正则表达式的使用

今日分享:正则表达式 一:正则表达式的定义及用途 正则表达式是一种特殊的字符串,字符串中的每个字符都含有特定的意义。使用者通过将正则中不同的字符组合成不同的字符...

1998
来自专栏一“技”之长

一个移动开发者的Mock数据之路 原

    在前端开发中,很大一部分工作都是将后台数据获取到后展示在前端界面上。如果接口是现成的,这个过程还相对容易一些,但是如果接口的开发和前端开发是同时进行的,...

731
来自专栏软件开发 -- 分享 互助 成长

分解成3NF的保持函数依赖的分解算法:

转换成3NF的保持函数依赖的分解算法: ρ={R1<U1,F1>,R2<U2,F2>,...,Rk<Uk,Fk>}是关系模式R<U,F>的一个分解,U={A1,...

3535
来自专栏Laoqi's Linux运维专列

正则扩展练习

grep命令的-P选项: 最典型的用法是,匹配指定字符串之间的字符。 比如,我们想在一句话(Hello,my name is aming.)中匹配中间的一段字符...

4136
来自专栏章鱼的慢慢技术路

Assets/FollowDestination.cs(6,13): error CS0246: The type or namespace name `NavMeshAgent' could not

2394
来自专栏deepcc

JavaScript检测IE浏览器(最短代码)

4228
来自专栏mathor

LeetCode130. 被围绕的区域

 bfs题,主函数中枚举每一个起点,如果是'O'就开始bfs搜索,首先将'O'变为'X',然后将周围是'O'都入队。这里有个地方要注意,如果'O'并不是被...

1002
来自专栏C/C++基础

Linux命令(12)——wc命令

(3)从文件读取输入文件名。如果有多个文件名,并且希望 wc 从一个文件中读取它们,那么使用-files0-from 选项。这里将文件名称必须以NULL字符结束...

1031
来自专栏一个会写诗的程序员的博客

一致性(连续性)hash算法(Consistent hashing)一致性(连续性)hash算法(Consistent hashing)

Consistent hashing is a scheme that provides hash table functionality in a way t...

1262
来自专栏丁科的专栏

pytorch 学习笔记之编写 C 扩展

在之前的文章中,我们已经了解了如何自定义 Module。这篇主要讲解pytorch利用 CFFI 进行 C 语言扩展。包括两个基本的步骤(docs):编写 C ...

9840

扫码关注云+社区

领取腾讯云代金券