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 知识体系总览

为了让你对本章内容有个整体印象,我画了一张图:

基础重试配置知识体系 @task 装饰器 retries 参数 控制重试次数 retry_delay_seconds 参数 控制重试间隔 失败后自动重试 最多 N 次 固定间隔等待 每次重试前等 N 秒 成功则取消后续重试 提前终止 retries 和 retry_delay_seconds 是重试配置的基石,务必掌握

2.6 什么时候该用基础重试?

不是所有任务都适合加重试。我总结了几条经验:

  • 适合加重试的场景:
    • 网络请求(HTTP 调用、API 请求)
    • 数据库操作(连接超时、死锁重试)
    • 文件操作(临时文件被占用)
    • 外部服务调用(第三方 API 偶尔不稳定)
  • 不适合加重试的场景:
    • 业务逻辑错误(比如参数校验失败,重试一万次也没用)
    • 数据一致性要求高的操作(比如扣款,重试可能导致重复扣款)
    • 幂等性无法保证的操作

一个小技巧:

如果你不确定某个任务该不该加重试,可以先设 retries=1, retry_delay_seconds=5。这样即使加错了,影响也很小。等观察一段时间后,再根据实际情况调整。

嗯,到这里基础重试配置就讲完了。你可能会觉得:「就两个参数,也太简单了吧?」别急,这只是开胃菜。后面我们会讲到更高级的重试策略,比如指数退避、基于异常类型的条件重试、还有重试回调函数。但万丈高楼平地起,先把这两个参数用好,你的流程稳定性就能提升一大截。