小编注
退避,指的是操作失败,等一段时间再重试,而不是立即重试的行为。其意义在于期望被调用的资源能够在这段时间里自行恢复。
退避和重试的函数装饰器
此模块提供函数装饰器,该函数装饰器可用于包装函数,以便函数重试直到满足某些条件为止。当访问可能出现间歇性故障的不可靠资源(如网络资源和外部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_exception和on_predicate通常在导入时确定参数。当关键字参数作为常量值传递时,这很好,但是假设我们想要查阅带有配置选项的字典,这些配置选项仅在运行时可用。相关值在导入时不可用。取而代之的是,装饰函数可以通过在运行时执行函数来获得参数值:
事件处理
两个backoff装饰器都可以选择使用关键字参数on_success、on_backoff和on_giveup接受事件处理程序函数。这在报告统计或执行其他自定义日志方面可能有用。
处理程序必须是一个接受一个字典参数的一元签名的可调用文件。此字典包含调用的详细信息。有效键包括:
打印回退事件的细节的处理程序可以实现如下:
每个事件类型的多个处理程序
在所有情况下,处理函数的迭代被接受,依次调用它们。例如,你可以提供一个简单的处理函数列表,作为on_backoff关键字参数的值:
获取异常信息
在on_exception装饰器的情况下,所有on_backoff和on_giveup处理程序都从异常块中调用,以处理异常。因此,异常信息可以通过python标准库(特别是sys.exc_info()或traceback模块)提供给处理函数。
异步代码
要在基于asyncio的异步代码中使用backoff,您只需要将backoff.on_exception
或backoff.on_predicate应用于协同例程。您还可以使用接口是相同的on_success、on_backoff和on_giveup事件处理程序的协同程序。
下面的示例使用aiohttp 异步HTTP Client/Server库。
采用Python 3.5及以上的async def和await语法:
如果使用Python 3.4,可以使用@asyncio.coroutine和yield from:
日志配置
错误和回退和重试尝试被记录到“退避”记录器。默认情况下,此记录器配置为NullHandler,因此除非配置处理程序,否则将没有输出。从编程上讲,这可以用简单的方法来完成:
记录默认级别是INFO,它对应于重试事件发生时的日志记录。如果您只希望在发生giveup事件时记录,则将记录器级别设置为ERROR。
英文原文:https://github.com/litl/backoff/
译者:张新英