【摸鱼笔记】熟悉Tenacity,让运行更稳固

背景

在程序运行过程中,经常遇到需要处理报错的情况,大部分报错情况重试就能解决;

作为流行的编程语言,python提供了用于简化处理捕捉报错并重试的库,Tenacity 便是其中之一。

简介

Tenacity 是一个 Apache 2.0 许可的通用重试库,用 Python 编写,用于简化向几乎任何事物添加重试行为的任务。

它具有如下特性:

  • 通用装饰器 API
  • 指定停止条件(即尝试次数限制)
  • 指定等待条件(即尝试之间的阻塞)
  • 自定义重试异常
  • 自定义对预期返回结果的重试
  • 重试协程
  • 使用上下文管理器重试代码块

安装

text 复制代码
pip install tenacity

基本用法

Tenacity的基本用法是装饰器,该装饰器可以应用于函数或方法以实现重试操作;

示例

python 复制代码
import tenacity


@tenacity.retry(stop=tenacity.stop_after_attempt(3))
def func_execute():
    print("the long way rounds...")
    raise Exception("Exception!")


try:
    func_execute()
except Exception as e:
    print(f"Exception: {e}")

运行结果

text 复制代码
the long way rounds...
the long way rounds...
the long way rounds...
Exception: RetryError[<Future at 0x1874323ddc0 state=finished raised Exception>]

func_execute函数中,手动抛出异常;

@tenacity.retry装饰器来修饰函数func_execute

如果@tenacity.retry中没有配置tenacity.stop_after_attempt将会无限循环;

示例中配置了重试策略,即在前三次尝试后停止重试tenacity.stop_after_attempt(3)

由于配置了重试,Tenacity将在抛出异常时重试该函数,最多重试3次,在运行结果中可以看到函数中的 print 执行了三次。

配置选项

除了重试次数,Tenacity还提供了许多其他配置选项;

以下是一些常用的配置选项

retry

定义在哪些异常情况下执行重试,可以根据异常类型等自定义的条件;

tenacity.retry_if_exception_type(exception_types)

捕捉到 与参数 exception_types 匹配的异常类型时重试

参数 exception_types :装饰对象抛出匹配异常后才会重试,其他的会忽略

示例

python 复制代码
import tenacity


@tenacity.retry(retry=tenacity.retry_if_exception_type(IOError),
                stop=tenacity.stop_after_attempt(5))
def func_execute(param: str):
    print(param)
    raise IOError("IOError")


try:
    func_execute("the long way rounds.")
except Exception as e:
    print(f"Exception: {e}")

运行结果

text 复制代码
the long way rounds.
the long way rounds.
the long way rounds.
the long way rounds.
the long way rounds.
Exception: RetryError[<Future at 0x20d5d03f0e0 state=finished raised OSError>]

tenacity.retry_if_result(predicate)

装饰器装饰对象返回 参数 predicate 匹配结果值 为True时重试

参数 predicate :用于判断返回值是否匹配的函数,其他的会忽略

示例

python 复制代码
import tenacity


def retry_decide(result: str) -> bool:
    # 此处 func_execute 返回值为 None,
    # 返回给retry_if_result的值将是True
    # 不管是否报错都会重试
    return result is None


@tenacity.retry(retry=tenacity.retry_if_result(retry_decide),
                stop=tenacity.stop_after_attempt(5))
def func_execute():
    print("执行 func_execute()")
    # 此处返回 None
    return None


try:
    result = func_execute()
except Exception as e:
    print(f"Exception: {e}")

运行结果

text 复制代码
the long way rounds.
the long way rounds.
the long way rounds.
the long way rounds.
the long way rounds.
Exception: RetryError[<Future at 0x20d5d03f0e0 state=finished raised OSError>]

stop

定义停止的条件,可以根据重试次数、重试时间等条件;

tenacity.stop_after_attempt(max_attempt_number:int)

参数 max_attempt_number :最大尝试次数,即超过尝试次数后抛出异常

示例

python 复制代码
import tenacity


@tenacity.retry(stop=tenacity.stop_after_attempt(5))
def func_execute():
    print("执行 func_execute()")
    raise Exception("抛出")


try:
    result = func_execute()
except Exception as e:
    print(f"Exception: {e}")
text 复制代码
执行 func_execute()
执行 func_execute()
执行 func_execute()
执行 func_execute()
执行 func_execute()
Exception: RetryError[<Future at 0x18ccf4fea80 state=finished raised Exception>]

tenacity.stop_after_delay(max_delay:int)

参数 max_delay :最大尝试时长(单位为秒),即超过尝试时长后抛出异常

示例

python 复制代码
import datetime as dt

import tenacity


@tenacity.retry(stop=tenacity.stop_after_delay(5),
                wait=tenacity.wait_fixed(2))
def func_execute():
    print("执行 func_execute()", dt.datetime.now().strftime("%H:%M:%S"))
    raise Exception("抛出")


try:
    result = func_execute()
except Exception as e:
    print(f"Exception: {e}")

运行结果

text 复制代码
执行 func_execute() 16:40:02
执行 func_execute() 16:40:04
执行 func_execute() 16:40:06
执行 func_execute() 16:40:08
Exception: RetryError[<Future at 0x1d6622ae810 state=finished raised Exception>]

wait

定义每次重试之间的等待时间,一般是固定的等待时间,也可以设置每次递增;

tenacity.wait_fixed(wait)

参数 wait :每次重试的间隔时间

示例

python 复制代码
import datetime as dt

import tenacity


@tenacity.retry(stop=tenacity.stop_after_attempt(2),
                wait=tenacity.wait_fixed(2))
def func_execute():
    print("执行 func_execute()", dt.datetime.now().strftime("%H:%M:%S"))
    raise Exception("抛出")


try:
    result = func_execute()
except Exception as e:
    print(f"Exception: {e}")

运行结果

text 复制代码
执行 func_execute() 16:41:19
执行 func_execute() 16:41:21
Exception: RetryError[<Future at 0x1e84b9ff4a0 state=finished raised Exception>]

tenacity.wait_random(min,max)

参数 min :随机间隔时间范围下限

参数 max :随机间隔时间范围上限

示例

python 复制代码
import datetime as dt

import tenacity


@tenacity.retry(stop=tenacity.stop_after_delay(10),
                wait=tenacity.wait_random(2, 5))
def func_execute():
    print("执行 func_execute()", dt.datetime.now().strftime("%H:%M:%S"))
    raise Exception("抛出")


try:
    result = func_execute()
except Exception as e:
    print(f"Exception: {e}")

运行结果

text 复制代码
执行 func_execute() 16:42:14
执行 func_execute() 16:42:16
执行 func_execute() 16:42:19
执行 func_execute() 16:42:22
执行 func_execute() 16:42:25
Exception: RetryError[<Future at 0x1bd3737ed50 state=finished raised Exception>]

before_sleep

在每次重试之前执行的操作,可以用于执行清理或日志记录等任务。

before_sleep_log(logger, log_level)

参数 logger :日志记录器对象

参数 log_level :日志记录等级 debug, info 等

示例

python 复制代码
import datetime as dt
import logging
import sys

import tenacity

logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)

logger = logging.getLogger(__name__)


@tenacity.retry(retry=tenacity.retry_if_exception_type(IOError),
                stop=tenacity.stop_after_attempt(3),
                wait=tenacity.wait_fixed(2),
                before_sleep=tenacity.before_sleep_log(logger, logging.DEBUG))
def func_execute(param: str):
    print(param, dt.datetime.now().strftime("%H:%M:%S"))
    raise IOError("IOError")


try:
    func_execute("the long way rounds.")
except Exception as e:
    print(f"Exception: {e}")

运行结果

text 复制代码
DEBUG:__main__:Retrying __main__.func_execute in 2.0 seconds as it raised OSError: IOError.
the long way rounds. 16:43:24
the long way rounds. 16:43:26
DEBUG:__main__:Retrying __main__.func_execute in 2.0 seconds as it raised OSError: IOError.
the long way rounds. 16:43:28
Exception: RetryError[<Future at 0x2357a3be090 state=finished raised OSError>]

reraise

是否重新引发异常,如果设置为True,则在达到最大重试次数后会引发原始异常。

示例

python 复制代码
import tenacity


@tenacity.retry(retry=tenacity.retry_if_exception_type(IOError),
                stop=tenacity.stop_after_attempt(3),
                reraise=True)
def func_execute(param: str):
    print(param)
    raise IOError("IOError")


try:
    func_execute("the long way rounds.")
except Exception as e:
    print(f"Exception: {e}")

运行结果

text 复制代码
the long way rounds.
the long way rounds.
the long way rounds.
Exception: IOError

组合重试条件

自定义重试策略,以满足指定条件下重试或停止重试。

示例

python 复制代码
import tenacity


# 在同一个 配置项中组合配置条件
@tenacity.retry(stop=tenacity.stop_after_delay(10) | tenacity.stop_after_attempt(5),
                retry=tenacity.retry_if_exception_type(IOError))
def func_execute():
    print("Operation with Custom Stop...")
    raise IOError("Failed!")


try:
    func_execute()
except Exception as e:
    print(f"Exception: {e}")
相关推荐
hef2884 小时前
如何生成特定SQL的AWR报告_@awrsqrpt.sql深度剖析单条语句性能
jvm·数据库·python
zjeweler4 小时前
“网安+护网”终极300多问题面试笔记-3共3-综合题型(最多)
笔记·网络安全·面试·职场和发展·护网行动
Jinkxs4 小时前
从语法纠错到项目重构:Python+Copilot 的全流程开发效率提升指南
python·重构·copilot
技术专家4 小时前
Stable Diffusion系列的详细讨论 / Detailed Discussion of the Stable Diffusion Series
人工智能·python·算法·推荐算法·1024程序员节
段一凡-华北理工大学4 小时前
【大模型+知识图谱+工业智能体技术架构】~系列文章01:快速了解与初学入门!!!
人工智能·python·架构·知识图谱·工业智能体
IT小Qi4 小时前
iperf3网络测试工具
网络·python·测试工具·信息与通信·ip
以神为界5 小时前
Python入门实操:基础语法+爬虫入门+模块使用全指南
开发语言·网络·爬虫·python·安全·web
xcjbqd05 小时前
Python API怎么加Token认证_JWT生成与验证拦截器实现
jvm·数据库·python
io_T_T5 小时前
如何调用google api 进行开发(使用免费版本)
python
广师大-Wzx6 小时前
一篇文章看懂MySQL数据库(下)
java·开发语言·数据结构·数据库·windows·python·mysql