Tenacity

关注
义项:Python重试库
Tenacity

Tenacity 是一个用 Python 编写的通用重试库(Retrying library), 采用 Apache 2.0 许可证发布, 目的是把失败后重新尝试的行为方便地加到各种调用上[1]。它源自对早期重试库 Retrying 的分支, 由朱利安·丹茹(Julien Danjou)维护, 提供了装饰器与应用编程接口两种用法, 并能指定停止条件、等待策略以及对异常和返回值的判定, 因而常被用于调用远程服务和分布式系统的场景[2]。

百科 图文
目录
  1. 定义
  2. 原理
  3. 发展历程
  4. 应用
  5. 局限
  6. 参见

定义

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]。

参见

  • Python —— Tenacity 以该语言编写并运行于其运行时之上。

  • 异步编程 —— 该库的异步用法与协程语法相关。

  • 指数退避 —— 该库内置的一种常见等待策略。

  • 装饰器 —— 该库最常用的接口形式。

参考资料

  1. Tenacity — Tenacity documentation [引用日期2026-10-14]
  2. tenacity [引用日期2026-10-14]
  3. Source: python-tenacity [引用日期2026-10-14]
  4. API Reference¶ [引用日期2026-10-14]
  5. Replace usage of 'retrying' with 'tenacity' [引用日期2026-10-14]
  6. Source: python-tenacity [引用日期2026-10-14]
  7. Package: python3-tenacity (9.1.4-2) [引用日期2026-10-14]
  8. python-tenacity-8.2.3-6.fc42 | Build Info [引用日期2026-10-14]
  9. Tenacity [引用日期2026-10-14]
  10. tenacity [引用日期2026-10-14]
词条评价
词条统计

浏览次数:2 次

阅读量:0 次 · 阅读完成量:0 次

最近更新:2026-10-13T21:13:49Z

历史版本

本条目引用的词条
Python 异步编程 指数退避 装饰器