如何在 FastAPI 中玩转 APScheduler,让任务定时自动执行?

1. APScheduler

1.1 什么是 APScheduler?

APScheduler 是一个轻量级但功能强大的 Python 定时任务调度库,支持固定时间间隔、特定时间点或 Cron 式任务调度。它能与 FastAPI 无缝集成,用于后台执行周期性任务(如数据清理、邮件发送、报表生成等),而不会阻塞主线程。

1.2 在 FastAPI 中集成 APScheduler

  • 后台任务自动化:避免手动触发重复性操作。
  • 资源解耦:将耗时任务从 API 请求中分离,提升响应速度。
  • 高可靠性:支持任务持久化和故障恢复机制。
  • 灵活调度:通过 Cron 表达式实现复杂时间规则。
graph TD A[FastAPI 启动] --> B[初始化 Scheduler] B --> C[添加定时任务] C --> D[任务执行] D --> E[任务结果处理] E --> F[关闭 Scheduler]

2. 基础集成步骤

2.1 安装依赖库

bash 复制代码
pip install fastapi==0.109.0
pip install uvicorn==0.27.0
pip install apscheduler==3.10.4
pip install pydantic==2.5.1

2.2 核心代码实现

python 复制代码
from fastapi import FastAPI
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.jobstores.memory import MemoryJobStore
from pydantic import BaseModel

# 定义 Pydantic 数据模型验证输入
class TaskRequest(BaseModel):
    task_name: str
    interval: int  # 单位:秒

app = FastAPI()
scheduler = AsyncIOScheduler(jobstores={"default": MemoryJobStore()})

@app.on_event("startup")
async def init_scheduler():
    """应用启动时初始化调度器"""
    scheduler.start()
    print("Scheduler started")

@app.on_event("shutdown")
async def shutdown_scheduler():
    """应用关闭时安全停止调度器"""
    scheduler.shutdown()
    print("Scheduler stopped")

@app.post("/schedule")
async def add_task(request: TaskRequest):
    """添加定时任务接口"""
    # 使用 Pydantic 自动校验 request 数据
    job = scheduler.add_job(
        execute_background_task,
        "interval",
        seconds=request.interval,
        args=[request.task_name],
        id=request.task_name
    )
    return {"status": "success", "job_id": job.id}

async def execute_background_task(task_name: str):
    """模拟后台任务执行"""
    print(f"Executing task: {task_name} at {datetime.utcnow()}")

代码说明:

  1. AsyncIOScheduler:基于 asyncio 的调度器,避免阻塞主线程。
  2. MemoryJobStore:默认内存存储(生产环境建议替换为 Redis 或 SQLAlchemy)。
  3. @app.on_event:生命周期钩子管理调度器启停。
  4. Pydantic 模型:自动校验 API 输入参数。

3. 高级应用场景

3.1 Cron 表达式调度

python 复制代码
# 添加每日凌晨执行的任务
scheduler.add_job(
    daily_report,
    "cron",
    hour=0,
    minute=5,
    id="daily_report"
)

3.2 异常处理与重试

python 复制代码
from apscheduler.events import EVENT_JOB_ERROR

def error_listener(event):
    """全局任务异常监听"""
    if event.exception:
        print(f"Job crashed: {event.job_id}, error: {event.exception}")

scheduler.add_listener(error_listener, EVENT_JOB_ERROR)

3.3 持久化存储(使用 SQLite)

python 复制代码
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore

jobstores = {
    "default": SQLAlchemyJobStore(url="sqlite:///jobs.sqlite")
}
scheduler = AsyncIOScheduler(jobstores=jobstores)

4. 课后 Quiz

  1. 问题 :如何在 APScheduler 中实现每周一上午 9 点执行任务?
    答案

    python 复制代码
    scheduler.add_job(task, "cron", day_of_week="mon", hour=9)
  2. 问题 :任务意外中止后如何自动恢复?
    答案

    使用持久化存储(如 SQLAlchemyJobStore)并在调度器初始化时添加 misfire_grace_time=300 参数。


5. 常见报错解决方案

报错 1:JobLookupError: No job by the id of ...

  • 原因:操作了不存在的任务 ID。

  • 解决方案

    python 复制代码
    # 操作前先检查任务是否存在
    if scheduler.get_job(job_id):
        scheduler.remove_job(job_id)

报错 2:MaxIterationsReached

  • 原因:Cron 任务迭代次数超出限制(通常因时间表达式错误导致)。
  • 预防措施
    • 使用在线工具校验 Cron 表达式(如 crontab.guru)。
    • 避免在循环中添加重复任务。

报错 3:RuntimeError: Scheduler not running

  • 原因:未正确初始化调度器生命周期。

  • 修复方法

    python 复制代码
    # 确保在 startup 事件中启动
    @app.on_event("startup")
    async def init():
        scheduler.start()
相关推荐
用户30750093037931 小时前
go Eino使用ADK开发agent
后端
唐叔在学习1 小时前
Python自动化指令进阶:UAC提权
后端·python
Assby1 小时前
Windows 在 PostgreSQL 上安装 vector 扩展
后端
12344521 小时前
【面试复盘】有了equals为什么还要hashcode
java·后端
小周在成长1 小时前
MyBatis 分页插件PageHelper
后端
Paladin_z1 小时前
Easy Query中间件的使用
后端
牛奔1 小时前
Go语言中结构体转Map优雅实现
开发语言·后端·macos·golang·xcode
掘金码甲哥2 小时前
我不允许谁还分不清这三种watch机制的区别
后端
张心独酌2 小时前
Rust新手练习案例库- rust-learning-example
开发语言·后端·rust
码事漫谈2 小时前
一文读懂“本体论”这个时髦词
后端