定义
Tenacity 是一个面向 Python 的通用重试库, 作用是在被调用的函数抛出异常或返回不符预期的结果时, 按照设定的规则自动再次执行该函数, 直到成功或达到停止条件[1]。它采用 Apache 2.0 许可证发布, 软件包的描述为「重试代码直到成功」[3]。
它同时提供装饰器形式的接口和可实例化的重试控制类, 使用者的代码不需要为每一个函数手写循环与延时逻辑[4]。库中的停止条件、等待策略、重试判定都被设计为可组合的对象, 可以用运算符连接后一起传入[2]。
原理
Tenacity 由若干正交的判定组件构成, 分别回答「是否重试」「何时停止」「每次等待多久」以及「重试前后执行什么动作」[4]。重试判定既可以基于异常类型, 也可以基于函数返回值, 例如仅在抛出 IOError 时重试, 或仅在返回值为 None 时重试, 多个条件可以用逻辑运算符组合[1]。
等待策略决定两次尝试之间的间隔。常见的做法包括固定间隔、随机间隔、指数退避, 以及把固定间隔与随机抖动相加, 用于缓解多个进程同时争抢同一资源造成的冲击[1]。指数退避的等待时间随尝试次数增长, 可写成 $t = m \cdot 2^{n}$, 其中 $n$ 为尝试序号, $m$ 为倍数参数, 并可通过上下限截断[1]。库还支持用 wait_chain 依次给出多段不同的等待方案[2]。
调用流程可以用下面的结构表示: 每次尝试前触发 before 回调, 尝试失败后触发 after 回调; 若判定应当继续重试, 则先调用 before_sleep 回调再休眠, 然后进入下一次尝试。全部尝试耗尽后默认抛出 RetryError; 若把 reraise 设为真, 则把最后一次的原始异常向上抛出[1]。
flowchart TD
A[调用函数] --> B{失败或结果不符预期}
B -- 否 --> C[返回结果]
B -- 是 --> D{满足停止条件}
D -- 是 --> E[抛出 RetryError 或原始异常]
D -- 否 --> F[按等待策略休眠]
F --> A
发展历程
Tenacity 起源于对早期重试库 Retrying 的分支。Retrying 的作者后来不再积极维护该仓库, 对提交的补丁也长期没有回应, 于是一个新的分支被建立起来并重新设计了接口[5][1]。Tenacity 与 Retrying 的接口并不兼容, 但补充了较多新功能并修复了若干长期存在的问题[1]。
两者在细节上存在差异: Tenacity 的等待时间以秒为单位, 而 Retrying 用毫秒; Tenacity 把停止、等待、重试判定设计成类, 装饰器与重试控制类的关键字参数也与之不同; 对于重试耗尽的调用, Tenacity 默认抛出 RetryError, 而 Retrying 会抛出函数最后一次的异常, 前者通过 reraise 参数提供兼容方式[5]。
开源项目 OpenStack 曾推动把其子项目中的重试实现从 Retrying 迁移到 Tenacity, 相关迁移工作围绕 2016 年前后展开[5]。此后 Tenacity 被多个 Linux 发行版收录并持续更新, 例如 Debian 中较早的打包版本为 4.12.0, 到 9.1.4 仍在维护; Fedora 42 收录的版本为 8.2.3[6][7][8]。
应用
Tenacity 主要用在需要与外部系统交互、且失败往往具有临时性的场合, 例如访问分布式服务和远程接口[1]。它既可以装饰同步函数, 也可以用于 异步编程 中的协程, 官方文档说明其支持 asyncio、Trio 与 Tornado 4.5 及以上版本的协程, 休眠也以异步方式完成[9]。
除装饰器之外, 库还允许直接实例化 Retrying 或 AsyncRetrying 对象, 以便用变量动态设置重试参数; 对于不适合拆成独立函数的代码块, 可以配合 for 循环与上下文管理器实现整块重试[1]。已装饰的函数还带有 retry_with 方法, 可在调用时临时替换停止条件等参数, 并通过 retry.statistics 读取本次重试的统计信息[9]。
日志记录方面, Tenacity 提供 before_log、after_log 与 before_sleep_log 等回调, 分别用于记录尝试开始、尝试失败以及即将休眠重试时的信息[10]。使用者也可以自定义回调, 回调接收一个 RetryCallState 对象, 其中包含函数、参数、尝试次数、结果、已等待时间等状态[9]。
局限
Tenacity 只负责按规则重复调用, 并不判断失败本身的原因。当失败属于权限不足、参数错误一类不会因重试而改变的情况时, 继续尝试通常没有意义, 这需要使用者自行设定恰当的重试判定[1]。
库的默认行为是在抛出异常时无限重试且不等待, 若不显式设置停止条件, 调用可能长时间无法返回[2]。同时, 重试会把最后一次的异常包裹在 RetryError 之中, 原始异常出现在堆栈中间, 排查问题时需要借助 reraise 参数才能让它出现在堆栈末尾[1]。
接口层面, Tenacity 与它所源自的 Retrying 不兼容, 从后者迁移需要调整关键字参数与调用方式[1]。此外, 默认的休眠实现基于 time.sleep, 在需要替换或模拟休眠的测试中, 需要在导入该库之前对其进行替换[5]。
参见
参考资料
- Tenacity — Tenacity documentation [引用日期2026-10-14]
- tenacity [引用日期2026-10-14]
- Source: python-tenacity [引用日期2026-10-14]
- API Reference¶ [引用日期2026-10-14]
- Replace usage of 'retrying' with 'tenacity' [引用日期2026-10-14]
- Source: python-tenacity [引用日期2026-10-14]
- Package: python3-tenacity (9.1.4-2) [引用日期2026-10-14]
- python-tenacity-8.2.3-6.fc42 | Build Info [引用日期2026-10-14]
- Tenacity [引用日期2026-10-14]
- tenacity [引用日期2026-10-14]