2、基础重试配置:@task装饰器中的retries参数、retry_delay_seconds参数、简单的重试示例
好,咱们正式开始动手配置重试。这一章我带你从最基础的参数入手,把 Prefect 的 @task 装饰器里那两个最常用的重试参数搞清楚。
说白了,重试机制就是告诉 Prefect:「这个任务如果失败了,别急着放弃,再给它几次机会。」我个人习惯在写任何可能不稳定的任务时,第一件事就是加上重试配置。你想想看,网络抖动、数据库连接池满、临时文件被锁——这些情况在线上太常见了。与其让整个流程卡死,不如让任务自己再试一次。
2.1 retries 参数:控制重试次数
retries 参数是最直观的。它告诉 Prefect:这个任务最多可以重试多少次。
核心要点:
retries=0(默认值):不重试,失败就失败。retries=3:最多重试 3 次。注意是「最多」,如果第 2 次就成功了,第 3 次不会执行。- 重试次数不包括首次执行。也就是说
retries=3意味着「首次执行 + 最多 3 次重试」,总共最多执行 4 次。
我在项目中遇到过一个问题:有个同事把 retries 设成了 10,觉得「多试几次总没错」。结果某个下游服务挂了,任务硬是重试了 10 次才放弃,白白浪费了 10 分钟。所以啊,重试次数不是越多越好,要根据业务场景来定。
2.2 retry_delay_seconds 参数:控制重试间隔
光有重试次数还不够。如果失败后立刻重试,大概率还是失败。这时候就需要 retry_delay_seconds 参数了。
这个参数指定每次重试前等待的秒数。比如 retry_delay_seconds=10,就是失败后等 10 秒再重试。
我的经验:
对于数据库连接超时这类问题,我一般设 5-10 秒。对于外部 API 限流,我可能会设 30-60 秒。具体数值取决于你遇到的是什么类型的错误。
这里有个细节要注意:retry_delay_seconds 是固定间隔。也就是说,每次重试前都等同样的时间。后面我们会讲到更高级的退避策略,但现阶段这个参数已经够用了。
2.3 简单的重试示例
光说不练假把式。咱们直接看代码。
from prefect import task, Flow
import random
import time
@task(retries=3, retry_delay_seconds=5)
def unstable_task():
"""模拟一个不稳定的任务,有 40% 的概率失败"""
time.sleep(1)
if random.random() < 0.4:
raise ValueError("哎呀,这次运气不好,失败了!")
return "任务成功完成!"
with Flow("重试示例流程") as flow:
result = unstable_task()
# 运行流程
flow.run()
这段代码做了什么?
unstable_task有 40% 的概率抛出异常。- 如果失败了,Prefect 会等 5 秒,然后自动重试。
- 最多重试 3 次。如果 3 次都失败,任务最终标记为失败。
- 如果中途某次成功了,后续重试取消。
运行一下,你会看到类似这样的输出:
[2023-10-01 10:00:00] INFO - prefect.TaskRunner | Task 'unstable_task': Starting...
[2023-10-01 10:00:01] ERROR - prefect.TaskRunner | Task 'unstable_task': ValueError: 哎呀,这次运气不好,失败了!
[2023-10-01 10:00:06] INFO - prefect.TaskRunner | Task 'unstable_task': Retrying (1/3)...
[2023-10-01 10:00:07] ERROR - prefect.TaskRunner | Task 'unstable_task': ValueError: 哎呀,这次运气不好,失败了!
[2023-10-01 10:00:12] INFO - prefect.TaskRunner | Task 'unstable_task': Retrying (2/3)...
[2023-10-01 10:00:13] INFO - prefect.TaskRunner | Task 'unstable_task': 任务成功完成!
看到了吗?第 2 次重试成功了,流程继续往下走。这就是重试的魅力。
2.4 两个参数配合使用的效果
咱们用一张表来总结一下这两个参数的行为:
| 参数组合 | 行为描述 |
|---|---|
retries=0 |
不重试,失败即终止 |
retries=3, retry_delay_seconds=0 |
失败后立即重试,最多 3 次 |
retries=3, retry_delay_seconds=10 |
失败后等 10 秒再重试,最多 3 次 |
retries=5, retry_delay_seconds=30 |
失败后等 30 秒再重试,最多 5 次 |
我曾经踩过的坑:
有一次我忘了设 retry_delay_seconds,结果任务失败后立刻重试,又失败又立刻重试…… 3 次重试在 1 秒内全部耗尽,问题根本没解决。所以记住:重试间隔和重试次数同样重要。
2.5 知识体系总览
为了让你对本章内容有个整体印象,我画了一张图:
2.6 什么时候该用基础重试?
不是所有任务都适合加重试。我总结了几条经验:
- 适合加重试的场景:
- 网络请求(HTTP 调用、API 请求)
- 数据库操作(连接超时、死锁重试)
- 文件操作(临时文件被占用)
- 外部服务调用(第三方 API 偶尔不稳定)
- 不适合加重试的场景:
- 业务逻辑错误(比如参数校验失败,重试一万次也没用)
- 数据一致性要求高的操作(比如扣款,重试可能导致重复扣款)
- 幂等性无法保证的操作
一个小技巧:
如果你不确定某个任务该不该加重试,可以先设 retries=1, retry_delay_seconds=5。这样即使加错了,影响也很小。等观察一段时间后,再根据实际情况调整。
嗯,到这里基础重试配置就讲完了。你可能会觉得:「就两个参数,也太简单了吧?」别急,这只是开胃菜。后面我们会讲到更高级的重试策略,比如指数退避、基于异常类型的条件重试、还有重试回调函数。但万丈高楼平地起,先把这两个参数用好,你的流程稳定性就能提升一大截。