Py学习  »  Python

Python退避及重试装饰器:backoff

Python程序员 • 5 年前 • 656 次点击  

小编注

退避,指的是操作失败,等一段时间再重试,而不是立即重试的行为。其意义在于期望被调用的资源能够在这段时间里自行恢复。

退避和重试的函数装饰器

此模块提供函数装饰器,该函数装饰器可用于包装函数,以便函数重试直到满足某些条件为止。当访问可能出现间歇性故障的不可靠资源(如网络资源和外部API)时,它应该有用。一般来说,它还可以用于外部生成内容的动态轮询资源。

装饰器既支持同步代码的常规功能,也支持异步代码的asyncio的协同程序。

示例

由于Kenneth Reitz的requests模块已经成为Python中同步HTTP客户端的行业标准,因此下面使用该模块编写了网络示例,但是backoff模块根本不需要它。

@backoff.on_exception

当指定异常被引发时,使用on_exception装饰器重试。这里有一个例子,当出现任何requests异常时,使用指数退避(即退避时间指数增长):

对于需要多个异常类型采取相同退避行为的情况,装饰器还将接受异常元组:

放弃的条件

可选参数可以指定要放弃的条件。

关键字参数max_time指定在放弃之前的总时间的最大秒数。

关键字参数max_tries指定在放弃之前对目标函数进行调用的最大次数。

在某些情况下,可能需要检查引发异常的实例本身,以确定它是否满足可重试条件。giveup关键字参数可用于指定一个函数,该函数接受异常并在不应该重试异常时返回真值:

当放弃事件发生时,问题中的异常重新引发,因此调用on_exception-decorated函数的代码可能仍然需要执行异常处理。

@backoff.on_predicate

当目标函数返回值符合某个特定条件时,on_predicate装饰器会安排重试。当为外部生成内容轮询资源时,这可能是有用的。

下面是一个例子,当目标函数的返回值为空列表时,使用斐波那契序列退避(退避时间满足):

在初始化等待生成器时传递额外的关键字参数,因此在初始化fibo生成器时,上面的max_value参数作为关键字参数传递。

当未指定断言函数时,默认做返回值的假值测试,因此上面可以写的更简洁些:

更简单地说,一个继续每秒钟轮询直到得到非假结果的函数可以像这样定义:

Jitter

jitter算法可以向任何一个退避装饰器提供jitter关键字参数。这个参数应该是一个函数,接受原始的退避值并返回它的jittered的对应项。

从1.2版开始,默认的jitter函数backoff.full_jitter实现了AWS架构博客的指数退避和Jitter帖子中定义的“Full Jitter”算法。注意,使用此算法,等待生成器所产生的时间实际上是等待的最大时间量。

以前版本的退避默认添加随机数毫秒(高达1s)到原始睡眠值。如果需要,这种行为现在可以作为backoff.random_jitter

使用多个装饰器

还可以组合退避装饰器以指定不同情况下的不同退避行为:

运行时配置

装饰函数on_exceptionon_predicate通常在导入时确定参数。当关键字参数作为常量值传递时,这很好,但是假设我们想要查阅带有配置选项的字典,这些配置选项仅在运行时可用。相关值在导入时不可用。取而代之的是,装饰函数可以通过在运行时执行函数来获得参数值:

事件处理

两个backoff装饰器都可以选择使用关键字参数on_successon_backoffon_giveup接受事件处理程序函数。这在报告统计或执行其他自定义日志方面可能有用。

处理程序必须是一个接受一个字典参数的一元签名的可调用文件。此字典包含调用的详细信息。有效键包括:

  • target:引用调用的函数或方法

  • args:func的位置参数

  • kwargs : func的关键字参数

  • tries: 到目前为止的调用次数

  • elapsed: 到现在为止经过的时间

  • wait: 等待秒(仅on_backoff处理)

  • value: 触发退避值(仅用on_predicate装饰器)

打印回退事件的细节的处理程序可以实现如下:

每个事件类型的多个处理程序

在所有情况下,处理函数的迭代被接受,依次调用它们。例如,你可以提供一个简单的处理函数列表,作为on_backoff关键字参数的值:

获取异常信息

on_exception装饰器的情况下,所有on_backoffon_giveup处理程序都从异常块中调用,以处理异常。因此,异常信息可以通过python标准库(特别是sys.exc_info()traceback模块)提供给处理函数。

异步代码

要在基于asyncio的异步代码中使用backoff,您只需要将backoff.on_exception backoff.on_predicate应用于协同例程。您还可以使用接口是相同的on_successon_backoffon_giveup事件处理程序的协同程序。

下面的示例使用aiohttp 异步HTTP Client/Server库。

采用Python 3.5及以上的async defawait语法:

如果使用Python 3.4,可以使用@asyncio.coroutine和yield from:

日志配置

错误和回退和重试尝试被记录到“退避”记录器。默认情况下,此记录器配置为NullHandler,因此除非配置处理程序,否则将没有输出。从编程上讲,这可以用简单的方法来完成:

记录默认级别是INFO,它对应于重试事件发生时的日志记录。如果您只希望在发生giveup事件时记录,则将记录器级别设置为ERROR。


英文原文:https://github.com/litl/backoff/
译者:张新英



今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/li3P4DAQTO
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/23379
 
656 次点击