人人都会AI编程

29.5 定时任务:APScheduler、crontab 脚本调度

更新时间:2026-07-12

在实际项目中,“定时执行某段代码”是非常常见的需求:每天凌晨生成报表、每小时拉取一次数据、每隔几分钟检查服务健康状态。Python 实现定时任务主要有两大流派:代码内调度(APScheduler)和系统级调度(crontab 配合脚本)。两种方式各有适用场景,下面分别介绍并给出真实可用的示例。


方案一:APScheduler(代码内调度)

是什么:APScheduler(Advanced Python Scheduler)是一个纯 Python 的定时任务库,支持在程序内部管理定时任务,不用依赖操作系统。它提供了 cron 式调度、固定间隔调度、一次性调度等多种方式,并且可以与常见 Web 框架(Flask、Django)集成。

为什么用它

  • 任务调度逻辑和业务代码写在一起,便于版本管理和部署。
  • 不依赖外部系统,适合在容器、Windows 等环境中直接运行。
  • 支持持久化(任务信息可存数据库),程序重启后任务不丢失。

安装

pip install apscheduler

实用示例:三种常用调度方式

from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime

def job():
    print(f"任务执行中... 当前时间: {datetime.now()}")

scheduler = BlockingScheduler()

# 1. 固定间隔执行(每 10 秒)
scheduler.add_job(job, 'interval', seconds=10)

# 2. cron 表达式执行(每天 08:30 执行)
scheduler.add_job(job, 'cron', hour=8, minute=30)

# 3. 一次性执行(程序启动 5 秒后执行)
scheduler.add_job(job, 'date', run_date=datetime.now() + timedelta(seconds=5))

scheduler.start()  # 会阻塞当前线程,直到手动停止

生产环境注意事项

  • 上面的 BlockingScheduler 会阻塞主线程,适合纯脚本。如果已经在 Web 框架或后台服务里,通常使用 BackgroundScheduler 在后台线程运行。
  • 可以结合 AsyncIOSchedulerasyncio 程序中使用,避免阻塞事件循环。
  • 任务函数里务必做好异常捕获,否则一个任务抛异常会导致后续同类任务停止调度。
  • 需要持久化时,可配置 MySQL/PostgreSQL 等后端,避免程序重启后丢失任务。

适用场景

  • 与业务逻辑耦合紧密的定时任务(如数据清理、缓存刷新)。
  • 需要在多个服务器上集中管理任务的场景(配合分布式调度后端)。
  • 开发环境或 Windows 服务器,不方便配置系统 crontab。

方案二:crontab + Python 脚本(系统级调度)

是什么:利用 Linux/Unix 系统自带的 cron 服务,按时间表达式调用你的 Python 脚本。Windows 下可以用“任务计划程序”替代。

为什么用它

  • 最经典、最稳定的方案,不占用 Python 进程常驻内存。
  • 任务调度独立于程序,即使 Python 程序挂了,cron 依然会按时唤起。
  • 适合简单的、与代码逻辑解耦的定时执行。

配置步骤(以 Linux 为例)

  1. 编写 Python 脚本,例如 /opt/scripts/report.py,并确保有可执行权限:
   #!/usr/bin/env python3
   # report.py - 日报生成脚本
   import pandas as pd
   from datetime import date
   
   today = date.today().isoformat()
   df = pd.DataFrame({'日期': [today], '销售额': [12345]})
   df.to_csv(f'/opt/reports/report_{today}.csv', index=False)
   
  1. 添加入 crontab
   crontab -e
   

加入一行(每天 8 点执行):

   0 8 * * * /usr/bin/python3 /opt/scripts/report.py >> /var/log/report.log 2>&1
   
  1. 检查是否生效
   crontab -l   # 列出当前用户任务
   

关键细节

  • 环境变量问题:cron 执行时的环境变量非常精简,可能找不到你的 Python 解释器或第三方库路径。最好在脚本首行用 #!/usr/bin/env python3 或者使用绝对路径 /usr/bin/python3。如果使用了虚拟环境,直接调用虚拟环境中的 Python 解释器,如 /home/user/venv/bin/python3
  • 日志记录:务必把输出重定向到日志文件(>> log 2>&1),否则任务出错也看不到报错信息。
  • 工作目录:cron 执行时的工作目录通常是用户的家目录,如果你的代码中使用了相对路径(如 open('data.csv')),可能找不到文件。建议在脚本内部用 os.chdir() 切换到脚本所在目录,或一律使用绝对路径。

适用场景

  • 简单的一次性定时任务,不用常驻进程。
  • 服务器巡检、备份、日志清理等系统管理类任务。
  • 任务数量不多、不要求动态修改调度规则的情况。

两种方案对比

| 维度 | APScheduler | crontab + 脚本 |
|--------------|---------------------------------|------------------------------|
| 管理复杂度 | 代码内控制,灵活修改 | 系统级配置,修改需登录服务器 |
| 环境依赖 | 依赖 Python 程序常驻运行 | 不常驻,按需唤起 |
| 平台兼容 | 跨平台(Linux/Windows/macOS) | 主要依赖 cron(Linux/Unix) |
| 任务持久化 | 支持持久化到数据库 | 无,修改需重写 crontab |
| 错误排查 | 日志在程序内统一输出 | 需单独配置重定向日志 |
| 适用规模 | 大量动态任务、与业务深度整合 | 少量、稳定的定时执行 |

真实建议

  • 如果你的 Python 应用本身就是长期运行的服务(如 Web 后端),把定时任务用 APScheduler 内置进去最方便,随应用一起部署和监控。
  • 如果是临时脚本、运维辅助任务,或者你希望将调度与程序解耦,直接用系统 crontab 更稳妥。
  • 无论用哪种方案,都要保证任务的幂等性(重复执行不会造成副作用),并做好失败重试和告警通知。