如果你正在用 Python 处理数据任务,特别是那些需要定时跑、出错要重试、任务之间有依赖关系、还要能随时看到执行状态的场景,Prefect 值得你花时间了解。它不是另一个“只能跑通 Demo”的框架,而是真正能让你的数据脚本具备生产级可靠性的工作流编排工具。
和常见的“写个脚本加个 crontab”相比,Prefect 最核心的价值是让你用很少的代码,就把一个普通 Python 函数变成带重试、日志、状态跟踪、依赖管理和可视化监控的完整工作流。你不用自己写重试逻辑、不用手动处理任务排队、不用折腾日志收集,这些 Prefect 都帮你封装好了。
下面我会按实际落地顺序,拆解怎么从零开始把一个本地脚本改造成 Prefect 工作流,包括环境准备、基础任务编写、参数调整、部署调度和常见问题排查。
1. 先搞清楚 Prefect 到底解决什么问题,再决定要不要用
很多人一听到“工作流编排”就觉得是大型数据平台才需要的东西,其实 Prefect 的定位很明确:让单个数据工程师或小团队能用 Python 快速构建可靠的数据管道。它特别适合这些场景:
1.1 你已经在用 Python 脚本处理数据,但遇到这些问题
- 手动重试很麻烦:脚本中途报错,你得重新跑,可能还要清理半成品数据
- 任务依赖靠“注释”管理:比如“先跑 A 脚本,再跑 B 脚本”,依赖关系只在你脑子里
- 执行状态看不见:脚本在后台跑,你不知道它是卡住了还是成功了
- 调度靠 crontab:修改执行时间要登录服务器改配置,没有集中管理
- 参数传递靠写死:不同环境要用不同参数,你得维护多个脚本版本
Prefect 的核心就是把这些琐事标准化、自动化。你用@flow和@task装饰器把现有函数包一层,它就自动获得了重试、日志、状态跟踪等能力。
1.2 Prefect 的两个核心概念:Flow 和 Task
- Task:代表一个最小工作单元,比如“下载数据”“清洗数据”“写入数据库”。用
@task装饰的函数就是一个 Task。 - Flow:代表一个完整工作流,由多个 Task 组成,定义它们之间的执行顺序和依赖关系。用
@flow装饰的函数就是一个 Flow。
这种设计的好处是,你可以先把重点放在单个 Task 的可靠性上(比如这个下载任务出错时应该重试几次),再通过 Flow 控制整体执行逻辑(比如清洗必须在下载完成后才能开始)。
1.3 什么情况下可能不需要 Prefect
如果你的数据任务满足以下条件,可能暂时不需要引入 Prefect:
- 脚本每次运行不超过几分钟,手动重试成本很低
- 任务之间没有复杂依赖,都是独立运行的
- 不需要精确的执行时间调度,每天跑一次就行
- 团队只有你一个人,不需要协作和状态共享
但如果你已经开始为“脚本又挂了”“不知道跑哪了”“参数传错了”这些问题头疼,Prefect 能帮你省下很多排查时间。
2. 环境准备和最小验证:先让一个简单任务跑起来
Prefect 支持 Python 3.10+,安装很简单,但我会建议你按这个顺序搭建环境,避免后续的依赖冲突。
2.1 创建独立的 Python 环境
# 用 conda 或 venv 创建独立环境 python -m venv prefect-env source prefect-env/bin/activate # Linux/macOS # prefect-env\Scripts\activate # Windows # 安装 Prefect pip install -U prefect为什么先创建独立环境:Prefect 的依赖比较多,特别是如果你要用到数据库连接、云存储等扩展,独立环境能避免和你现有项目的依赖冲突。
2.2 验证安装和基础功能
创建一个最简单的测试脚本test_prefect.py:
from prefect import flow, task import time @task def hello_task(): print("Hello from Prefect task!") return "Hello" @flow(name="My First Flow") def hello_flow(): result = hello_task() print(f"Flow received: {result}") if __name__ == "__main__": hello_flow()运行这个脚本:
python test_prefect.py你应该看到输出:
Hello from Prefect task! Flow received: Hello这个简单的验证能确认:
- Prefect 基础包安装成功
@task和@flow装饰器工作正常- 任务和流的基本执行逻辑没问题
2.3 启动本地 UI 查看执行记录
Prefect 的一个关键功能是可视化监控。启动本地服务器:
prefect server start然后在浏览器打开 http://localhost:4200,你会看到 Prefect 的 Web UI。虽然我们刚才运行的简单任务不会自动显示在这里(需要部署后才能看到),但这个步骤能验证服务器功能是否正常。
如果 prefect server start 报错,常见原因和解决:
- 端口冲突:4200 端口被占用,可以加
--port 其他端口号 - 数据库连接问题:首次启动会创建本地数据库,确保有写权限
- 依赖缺失:重新安装 prefect 或检查 Python 环境
3. 把真实的数据脚本改造成 Prefect 工作流
现在我们来处理一个更实际的场景:定期获取 GitHub 仓库信息。这是 Prefect 文档中的示例,但我会补充更多实际落地时的细节。
3.1 原始脚本的 Prefect 化改造
假设你原来有一个获取 GitHub 星数的脚本:
import httpx def get_stars(repo): """获取 GitHub 仓库星数""" url = f"https://api.github.com/repos/{repo}" response = httpx.get(url) if response.status_code == 200: stars = response.json()["stargazers_count"] print(f"{repo} has {stars} stars!") return stars else: print(f"Failed to get stars for {repo}") return None def main(): repos = ["PrefectHQ/prefect", "pandas-dev/pandas", "apache/spark"] for repo in repos: get_stars(repo) if __name__ == "__main__": main()用 Prefect 改造后:
from prefect import flow, task import httpx from typing import List, Optional @task( name="Get GitHub Stars", # 给任务起个有意义的名字 log_prints=True, # 自动记录打印内容 retries=2, # 失败时重试2次 retry_delay_seconds=10 # 重试间隔10秒 ) def get_stars(repo: str) -> Optional[int]: """获取 GitHub 仓库星数""" try: url = f"https://api.github.com/repos/{repo}" response = httpx.get(url, timeout=30.0) # 添加超时控制 response.raise_for_status() # 非200状态码会抛异常 stars = response.json()["stargazers_count"] print(f"{repo} has {stars} stars!") return stars except Exception as e: print(f"Error getting stars for {repo}: {e}") return None @flow( name="GitHub Stars Monitor", description="定期监控多个 GitHub 仓库的星数变化" ) def github_stars_flow(repos: List[str]): """监控 GitHub 星数的工作流""" results = {} for repo in repos: # 每个仓库的获取任务会按顺序执行 stars = get_stars(repo) results[repo] = stars # 可以在这里添加后续处理,比如保存结果到文件或数据库 print(f"监控完成,结果: {results}") return results if __name__ == "__main__": # 测试运行 repositories = ["PrefectHQ/prefect", "pandas-dev/pandas", "apache/spark"] github_stars_flow(repositories)3.2 关键改造点解析
1. 错误处理与重试机制
@task(retries=2, retry_delay_seconds=10)- 网络请求可能因为临时故障失败,自动重试能提高成功率
- 重试次数不是越多越好,2-3次通常足够应对临时问题
- 重试间隔避免太短,给外部服务恢复时间
2. 超时控制
httpx.get(url, timeout=30.0)- 没有超时的网络请求可能永远挂起
- 30秒对于 GitHub API 是合理值,根据你的实际接口调整
3. 类型注解
def get_stars(repo: str) -> Optional[int]:- 虽然不是必须,但类型注解能让代码更清晰
- Prefect 能利用类型信息做更好的参数验证
4. 有意义的命名
@flow(name="GitHub Stars Monitor", description="...")- 在 UI 中能快速识别不同工作流
- 描述信息帮助团队成员理解这个 Flow 的用途
3.3 运行并观察效果
现在运行改造后的脚本,你会注意到:
- 自动重试:如果 GitHub API 暂时不可用,任务会自动重试2次
- 详细日志:每个任务的开始、结束、重试都有日志记录
- 结构化输出:返回结果被收集到字典中,便于后续处理
但此时任务记录还不会出现在 UI 中,因为这是直接运行(ephemeral execution),不是部署后的运行。接下来我们解决部署问题。
4. 部署和调度:让工作流自动运行
直接运行脚本适合测试,生产环境需要部署成长期运行的服务。Prefect 提供几种部署方式,我们从最简单的本地调度开始。
4.1 创建本地部署
修改脚本的最后部分:
if __name__ == "__main__": # 创建部署,每分钟运行一次 github_stars_flow.serve( name="github-stars-production", description="生产环境 GitHub 星数监控", cron="* * * * *", # 每分钟执行 parameters={"repos": ["PrefectHQ/prefect", "pandas-dev/pandas"]}, tags=["monitoring", "github"] # 用标签分类管理 )运行这个脚本:
python github_stars.py这次不会立即执行工作流,而是会启动一个长期运行的服务,它会:
- 在后台监听调度指令
- 每分钟自动执行一次
github_stars_flow - 将执行记录发送到 Prefect UI
4.2 在 UI 中查看和管理部署
现在刷新 Prefect UI (http://localhost:4200),你应该能看到:
- Deployments页面出现
github-stars-production部署 - Flow Runs页面每分钟会新增一条运行记录
- 点击具体运行记录能看到每个任务的详细状态、日志、执行时间
部署参数的详细解释:
github_stars_flow.serve( name="github-stars-production", # 部署名称,在UI中显示 description="生产环境监控", # 详细描述 cron="* * * * *", # 调度规则,支持cron表达式 interval=300, # 或者用秒数间隔,与cron二选一 parameters={"repos": [...]}, # 每次运行的固定参数 tags=["monitoring", "github"], # 标签,便于筛选和分类 version="v1.0" # 版本号,跟踪迭代历史 )4.3 手动触发和参数覆盖
除了自动调度,你还可以:
通过 UI 手动触发:
- 在 Deployments 页面点击 "Quick Run"
- 可以覆盖默认参数,比如临时监控其他仓库
通过 CLI 触发:
prefect deployment run "GitHub Stars Monitor/github-stars-production"带参数触发:
prefect deployment run "GitHub Stars Monitor/github-stars-production" -p '{"repos": ["PrefectHQ/prefect"]}'这种灵活性让你能在保持自动调度的同时,随时手动执行特定任务。
5. 高级特性:让工作流更健壮
基础功能跑通后,这些高级特性能让你的数据管道真正具备生产可靠性。
5.1 任务依赖和并行执行
前面的例子是顺序执行,但 Prefect 支持复杂的依赖关系:
@flow def data_pipeline_flow(): # 这些任务可以并行执行,因为它们没有依赖关系 raw_data = download_data.submit() schema_info = get_schema.submit() # 这个任务要等前面两个都完成 validated_data = validate_data.submit(raw_data, schema_info) # 等待所有任务完成 result = validated_data.result() return result使用.submit()方法让任务异步执行,Prefect 会自动管理依赖关系。
5.2 条件执行和动态工作流
工作流可以根据运行时结果决定执行路径:
@task def check_data_quality(data): return len(data) > 1000 # 示例质量检查 @flow def smart_pipeline_flow(): data = download_data() if check_data_quality(data): # 数据质量好,执行完整处理 cleaned = clean_data(data) result = analyze_data(cleaned) else: # 数据质量差,只执行基础处理 result = basic_processing(data) # 还可以发送警报 send_alert("数据质量不足") return result5.3 缓存和避免重复执行
对于耗时的计算任务,可以启用缓存:
@task(cache_key_fn=lambda *args, **kwargs: "static_key", cache_expiration=3600) # 缓存1小时 def expensive_computation(data): # 耗时计算... return result或者基于输入参数缓存:
@task(cache_key_fn=lambda task, context: f"compute-{context['parameters']['x']}") def parameterized_compute(x: int): return x * x5.4 错误处理和回退机制
除了重试,还可以实现更精细的错误处理:
@task(retries=2) def primary_method(): # 主要方法 pass @task def fallback_method(): # 备用方法 pass @flow def robust_flow(): try: result = primary_method() except Exception as e: print(f"主要方法失败: {e}, 尝试备用方法") result = fallback_method() return result6. 生产环境部署考量
当你要把 Prefect 应用到真实生产环境时,需要关注这些方面。
6.1 基础设施选择
开发/测试环境:
- 本地 Prefect server(我们刚才用的)
- SQLite 数据库,单进程运行
小型生产环境:
- Prefect server + PostgreSQL
- 部署在单台服务器或容器中
中型以上生产环境:
- Prefect Cloud(托管服务)
- 或者自托管的 Prefect server 集群
- 使用 Prefect agent 管理任务执行
6.2 配置管理
不要硬编码配置,使用 Prefect 的配置系统:
from prefect import settings # 使用环境变量或配置文件 database_url = settings.get("PREFECT_API_DATABASE_CONNECTION_URL") logging_level = settings.get("PREFECT_LOGGING_LEVEL", "INFO")创建prefect.toml配置文件:
[api] database_connection_url = "postgresql://user:@localhost/prefect" [logging] level = "INFO" [server] ui_api_url = "http://localhost:4200/api"6.3 安全考虑
- 数据库安全:生产环境用 PostgreSQL,配置正确的访问控制
- API 安全:如果公开访问 UI,需要配置认证
- 敏感信息:使用 Prefect 的 secret 管理功能,不要硬编码密码密钥
from prefect import get_client from prefect.context import get_settings # 安全地获取敏感信息 api_key = get_settings().github_api_key7. 常见问题排查指南
实际使用中可能会遇到这些问题,这是我的排查经验。
7.1 任务一直处于排队状态
可能原因:
- 没有可用的 agent 执行任务
- agent 配置错误,无法连接到 server
- 任务资源要求超过 agent 能力
排查步骤:
- 检查 agent 状态:
prefect agent status - 确认 agent 与 server 连接正常
- 查看任务日志,看是否有资源约束错误
7.2 任务执行失败但日志没有错误信息
可能原因:
- 任务进程被外部杀死(内存不足、超时)
- 依赖包版本冲突
- 权限问题
排查步骤:
- 检查系统日志(如 docker 日志、系统日志)
- 在任务开始时记录环境信息(Python 版本、包版本)
- 测试最小可复现案例,排除依赖问题
7.3 UI 中看不到执行记录
可能原因:
- server 和 agent 版本不匹配
- 数据库连接问题
- 网络连通性问题
排查步骤:
- 确认 server 和 agent 版本一致
- 检查 server 日志看是否有数据库错误
- 测试 agent 到 server 的网络连接
7.4 任务执行速度比预期慢
可能原因:
- 资源竞争(CPU、内存、IO)
- 网络延迟
- 不必要的序列化/反序列化
优化建议:
- 使用
@task(persist_result=False)关闭结果持久化(如果不需要) - 优化任务粒度,避免单个任务太大
- 使用更高效的序列化格式
8. 与其他工具的比较和选型建议
了解 Prefect 在生态中的位置,帮你做出更好的技术选型。
8.1 Prefect vs. Airflow
选择 Prefect 当:
- 你主要用 Python,希望用代码定义一切
- 需要动态工作流(运行时决定执行路径)
- 想要更简单的本地开发和测试体验
- 团队规模较小,需要快速迭代
选择 Airflow 当:
- 需要与其他大数据生态工具深度集成
- 团队已经熟悉 Airflow 的 DAG 概念
- 有专门的运维团队管理调度基础设施
8.2 Prefect vs. 自定义脚本 + crontab
Prefect 的优势:
- 自动重试和错误处理
- 完整的执行历史和日志
- 可视化的监控界面
- 任务依赖管理
- 参数化执行和版本控制
坚持 crontab 当:
- 任务非常简单,几乎不会失败
- 不需要复杂的依赖关系
- 对监控要求很低,日志文件足够
- 资源受限,无法部署额外服务
8.3 什么时候该考虑升级到 Prefect Cloud
考虑 Prefect Cloud 当:
- 团队多人协作,需要权限管理
- 需要高可用性的调度服务
- 不想自己维护 Prefect server 基础设施
- 需要更高级的监控和告警功能
坚持自托管当:
- 数据安全要求高,不能上云
- 已经有稳定的基础设施团队
- 预算有限,Prefect Cloud 成本过高
我个人更建议从本地 Prefect server 开始,等真正体会到它的价值后再考虑是否升级到 Cloud 版本。很多团队的需求用自托管的 server 版本就足够了。
Prefect 最大的价值在于它让数据工作流的可靠性从"需要大量自定义开发"变成了"几行配置就能搞定"。如果你经常需要处理定时数据任务,花一天时间试试 Prefect,可能会帮你省下未来大量的脚本维护时间。