You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Heroku托管Flask应用APScheduler定时任务重复执行问题求助

问题根因
  • Gunicorn默认多进程启动:你当前的Procfile中web进程启动命令为web: gunicorn flasky:app,未指定worker数量,Gunicorn默认会按「2*CPU核心数+1」的规则启动多个worker进程。每个worker进程启动时都会独立执行create_app逻辑,你写的if not scheduler.running判断仅能在同进程内生效,不同进程的scheduler实例完全隔离,因此会启动多个独立的BackgroundScheduler实例,所有实例都从同一个SQLAlchemyJobStore拉取任务执行,直接导致任务重复触发。
  • 额外的scheduler实例来源:你的tasks.py顶部直接调用了create_app初始化应用,RQ worker启动加载tasks模块时也会初始化一个scheduler实例,相当于多了一个额外的任务执行入口。

你看到的刷新页面任务信息跳动,就是因为请求被分发到了不同的gunicorn worker进程,每个进程返回的是自己持有的scheduler实例的任务数据,才会出现两个值来回跳的情况。

解决方案

方案1(最稳定,推荐):拆分调度逻辑到独立进程

  1. 调整应用初始化逻辑:修改app/__init__.py中的create_app函数,删除所有scheduler相关的初始化、启动代码,把这部分逻辑单独抽成独立的启动脚本(比如scheduler.py),仅在专门的调度进程中运行。
  2. 调整tasks模块:删除app/tasks.py顶部的app = create_app(os.getenv('FLASK_CONFIG') or 'default')代码,需要用到Flask应用上下文的逻辑,改为在任务函数内部手动push上下文,避免加载tasks模块时就触发scheduler初始化。
  3. 调整Procfile配置,新增单独的调度进程:
web: gunicorn flasky:app
worker: rq worker -u $REDIS_URL flasky
scheduler: python scheduler.py

调整后在Heroku后台将scheduler dyno的实例数设为1,确保全局只有一个scheduler实例运行,从根源避免多实例问题。

方案2(低成本,临时解决):限制进程数量+移除多余初始化

如果不想额外增加dyno成本,可以先做两处修改快速验证修复:

  1. 修改web进程启动命令,强制Gunicorn仅启动1个worker:
web: gunicorn --workers=1 flasky:app
  1. 删除app/tasks.py顶部的create_app调用,修改为函数内按需加载上下文。
    修改完成后重新部署即可解决重复执行问题。

方案3:替换调度实现

直接弃用自行维护的BackgroundScheduler,改用Heroku官方的Scheduler插件触发定时任务,或者改用RQ的定时任务扩展rq-scheduler,和你当前在用的RQ worker生态完全适配,不需要额外维护两套调度逻辑。

内容的提问来源于stack exchange,提问作者emvy03

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.18 19:45:07