说明
官方文档:https://tenacity.readthedocs.io/en/latest/
Tenacity 是一个 Apache 2.0 许可的通用重试库,用 Python 编写,用于简化将重试行为添加到几乎任何内容的任务。它起源于 retrying
的一个分支,遗憾的是retrying
不再 维护。Tenacity 与retrying
不兼容,但添加了重要的新功能并修复了许多长期存在的错误。
特征
通用
装饰器 API- 指定
停止条件
(即限制尝试次数) - 指定
等待条件
(即尝试之间的指数退避休眠) - 自定义对
预期返回结果
的重试 停止
、等待
、重试
条件任意组合
协程 重试
- 使用
上下文管理器
重试代码块
安装: pip install tenacity
例子
不加任何条件,
一直重试
到正确结果
,才停止重试
import random from tenacity import retry @retry def fun(): r = random.randint(1, 3) print(f'当前 {r}') assert r == 2 if __name__ == '__main__': fun() ----------------------------------- 当前 1 当前 3 当前 3 当前 2 Process finished with exit code 0
停止条件
stop_after_attempt
比较常用,停止条件还有很多
from tenacity import retry from tenacity.stop import stop_after_attempt # 重试 2 次,停止 @retry(stop=stop_after_attempt(2)) def fun(): pass
等待条件
from tenacity import retry # 每次重试等待 2秒 from tenacity.wait import wait_fixed @retry(wait=wait_fixed(2)) def fun(): # 每次重试 随机等待 1~2秒 from tenacity.wait import wait_random @retry(wait=wait_random(min=1, max=2)) def fun(): # 在重试分布式服务和其他远程端点时,结合固定等待和抖动(以帮助避免雷鸣般的群体) # 固定基础等待 3 秒,随机增加 0~2 秒 from tenacity.wait import wait_fixed, wait_random @retry(wait=wait_fixed(3) + wait_random(0, 2)) def fun(): # 生成一个等待链(等待时间的列表)等待时间相对于:[3,3,3,7,7,9] from tenacity.wait import wait_chain, wait_fixed @retry(wait=wait_chain(*[wait_fixed(3) for i in range(3)] + [wait_fixed(7) for i in range(2)] + [wait_fixed(9)])) def fun():
wait_exponential 重试分布式服务和其他远程端点时,
等待时间
呈指数增长
- multiplier: 增长的指数【默认值
1
】 - min: 最小等待时间 【默认值
0
】 - max: 最大等待时间【默认值
sys.maxsize / 2
】
import random from loguru import logger from tenacity import retry from tenacity.wait import wait_exponential @retry(wait=wait_exponential(multiplier=1, min=4, max=10)) def fun(): r = random.randint(1, 5) logger.info(f'当前 {r}') assert r == 6 if __name__ == '__main__': fun() ----------------------------------------------- 2022-02-28 10:52:37.918 | INFO | __main__:fun:10 - 当前 2 2022-02-28 10:52:41.930 | INFO | __main__:fun:10 - 当前 2 2022-02-28 10:52:45.938 | INFO | __main__:fun:10 - 当前 1 2022-02-28 10:52:49.949 | INFO | __main__:fun:10 - 当前 4 2022-02-28 10:52:57.952 | INFO | __main__:fun:10 - 当前 3 2022-02-28 10:53:07.965 | INFO | __main__:fun:10 - 当前 1 2022-02-28 10:53:17.975 | INFO | __main__:fun:10 - 当前 4 2022-02-28 10:53:27.977 | INFO | __main__:fun:10 - 当前 3
stop_after_delay 函数运行
超过限制秒数
,引发重试异常
import random import time from tenacity import retry from tenacity.wait import stop_after_delay @retry(stop=stop_after_delay(2)) def fun(): r = random.randint(1, 5) print(f'当前 {r}') time.sleep(2) assert r == 2 print(f'对了') ----------------------------------- tenacity.RetryError: RetryError[<Future at 0x260529a62b0 state=finished raised AssertionError>]
是否重试
# AssertionError 异常,触发重试 from tenacity.retry import retry_if_exception_type @retry(retry=retry_if_exception_type(AssertionError)) def fun():
使用
函数判断
,是否重试
import random from loguru import logger from tenacity import retry from tenacity.retry import retry_if_result @retry(retry=retry_if_result(lambda x: x != 2)) def fun(): r = random.randint(1, 5) logger.info(f'当前 {r}') return r if __name__ == '__main__': fun() -------------------------------------------- 2022-02-28 11:26:33.588 | INFO | __main__:fun:10 - 当前 5 2022-02-28 11:26:33.588 | INFO | __main__:fun:10 - 当前 1 2022-02-28 11:26:33.588 | INFO | __main__:fun:10 - 当前 3 2022-02-28 11:26:33.588 | INFO | __main__:fun:10 - 当前 2
组合
重试条件
import random from loguru import logger from tenacity import retry from tenacity.retry import retry_if_result, retry_if_exception_type """ 函数结果不可能为 2 和 3 - 2 重试 - 3 触发 AssertionError 异常,重试 """ @retry(retry=(retry_if_result(lambda x: x != 2) and retry_if_exception_type(AssertionError))) def fun(): r = random.randint(1, 5) logger.info(f'当前 {r}') if r == 3: raise AssertionError(f'r is 3') return r if __name__ == '__main__': logger.info(f'fun {fun()}')
协程重试
import random import asyncio from loguru import logger from tenacity import retry from tenacity.wait import wait_fixed @retry(wait=wait_fixed(2)) async def fun(): r = random.randint(1, 3) logger.info(f'当前 {r}') assert r == 2 if __name__ == '__main__': asyncio.run(fun()) ----------------------------------------- 2022-02-28 12:36:07.801 | INFO | __main__:fun:11 - 当前 3 2022-02-28 12:36:09.805 | INFO | __main__:fun:11 - 当前 1 2022-02-28 12:36:11.807 | INFO | __main__:fun:11 - 当前 3 2022-02-28 12:36:13.814 | INFO | __main__:fun:11 - 当前 2
统计数据
函数.retry.statistics
- 重试次数
- 开始运行时间
- 整个重试的耗时
import random from loguru import logger from tenacity import retry from tenacity.retry import retry_if_result @retry(reraise=True, retry=retry_if_result(lambda x: x != 2)) def fun(): r = random.randint(1, 5) logger.info(f'当前 {r}') return r if __name__ == '__main__': logger.info(f'fun {fun()}') logger.info(f'fun {fun.retry.statistics}') ---------------------------------------------------- 2022-02-28 11:43:33.558 | INFO | __main__:fun:10 - 当前 1 2022-02-28 11:43:33.558 | INFO | __main__:fun:10 - 当前 1 2022-02-28 11:43:33.558 | INFO | __main__:fun:10 - 当前 1 2022-02-28 11:43:33.559 | INFO | __main__:fun:10 - 当前 3 2022-02-28 11:43:33.559 | INFO | __main__:fun:10 - 当前 5 2022-02-28 11:43:33.559 | INFO | __main__:fun:10 - 当前 4 2022-02-28 11:43:33.559 | INFO | __main__:fun:10 - 当前 2 2022-02-28 11:43:33.559 | INFO | __main__:<module>:15 - fun 2 2022-02-28 11:43:33.559 | INFO | __main__:<module>:16 - fun {'start_time': 6972.546, 'attempt_number': 7, 'idle_for': 0, 'delay_since_first_attempt': 0.0}
重试代码块
重试代码块的逻辑包含的一个
for 循环
中,然后包含
一个上下文管理器
,使需要重试
的逻辑脱离函数
import random from loguru import logger from tenacity import Retrying, RetryError, stop_after_attempt try: # 重试 3 次 for attempt in Retrying(stop=stop_after_attempt(3)): # 重试、等待、停止 条件设置 with attempt: # 重试逻辑 包含在 上下文管理器 r = random.randint(1, 3) logger.info(f'r -> {r}') assert r == 2 except RetryError: logger.error(f'超出重试次数') # 超出重试次数处理逻辑 pass ---------------------------------------------------------- 2022-02-28 13:08:13.491 | INFO | __main__:<module>:10 - r -> 3 2022-02-28 13:08:13.492 | INFO | __main__:<module>:10 - r -> 3 2022-02-28 13:08:13.492 | INFO | __main__:<module>:10 - r -> 3 2022-02-28 13:08:13.492 | ERROR | __main__:<module>:13 - 超出重试次数
异步重试代码块
异步使用
import random import asyncio from loguru import logger from tenacity import AsyncRetrying, RetryError, stop_after_attempt async def fun(): try: async for attempt in AsyncRetrying(stop=stop_after_attempt(3)): # 重试、等待、停止 条件设置 # 这个 with 前不能带 async with attempt: # 重试逻辑 包含在 上下文管理器 r = random.randint(1, 3) logger.debug(f'r -> {r}') assert r == 2 except RetryError: logger.error('达到重试上限') pass if __name__ == '__main__': asyncio.run(fun()) ------------------------------------------------------------------ 2022-02-28 13:09:17.247 | DEBUG | __main__:fun:13 - r -> 3 2022-02-28 13:09:17.247 | DEBUG | __main__:fun:13 - r -> 1 2022-02-28 13:09:17.247 | DEBUG | __main__:fun:13 - r -> 3 2022-02-28 13:09:17.247 | ERROR | __main__:fun:16 - 达到重试上限
版权声明:《 【PY模块】Tenacity 重试 》为明妃原创文章,转载请注明出处!
最后编辑:2022-3-6 11:03:24