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

FastAPI使用Gunicorn启动多worker时如何仅在单个worker运行定时任务

多worker场景下定时任务单实例运行的实现方案

Gunicorn启动的多个worker是完全独立的进程,没有内置的进程间协调能力,所以必须通过外部锁、角色标记或者独立进程的方式实现定时任务仅单实例运行,以下是三种常用实现方案:

方案1:基于分布式锁实现(改造成本最低)

  • 选择通用中间件作为锁存储,推荐用Redis,数据库、Memcached也可实现
  • 所有worker的定时任务触发时,先尝试获取全局唯一锁,只有成功拿到锁的worker才执行任务逻辑,执行完成后主动释放锁
  • 必须给锁设置过期时间,避免worker异常崩溃导致锁永久无法释放
    Python+Redis实现示例:
import redis
from apscheduler.schedulers.background import BackgroundScheduler

redis_client = redis.Redis(host='你的Redis地址', port=6379, db=0)
# 全局唯一锁的key
LOCK_KEY = "scheduler_unique_lock"
# 锁过期时间要大于任务的最大预期执行时长,单位秒
LOCK_EXPIRE = 60

def your_scheduled_task():
    # nx参数表示只有key不存在时才设置成功,ex为过期时间
    get_lock = redis_client.set(LOCK_KEY, "running", ex=LOCK_EXPIRE, nx=True)
    if not get_lock:
        # 没拿到锁直接退出
        return
    try:
        # 此处写你的定时任务逻辑
        print("仅单worker执行的任务逻辑")
    finally:
        # 任务执行完成主动释放锁
        redis_client.delete(LOCK_KEY)

# 所有worker都可以正常初始化调度器,不会重复执行
scheduler = BackgroundScheduler()
scheduler.add_job(your_scheduled_task, 'cron', hour=2)
scheduler.start()

方案2:给worker加角色标记

  • 不一次性启动4个worker,分开启动普通业务worker和定时任务专属worker,通过环境变量给专属worker打角色标记
  • 代码中判断当前进程的环境变量,只有带定时任务角色标记的worker才会初始化启动调度器
    启动命令示例:
# 启动3个处理业务请求的普通worker
gunicorn main:app --workers 3 --bind 0.0.0.0:8000
# 单独启动1个带定时任务角色标记的worker
GUNICORN_ROLE=scheduler gunicorn main:app --workers 1 --bind 0.0.0.0:8000

代码判断逻辑:

import os
from apscheduler.schedulers.background import BackgroundScheduler

# 只有标记为定时任务角色的worker才会启动调度器
if os.getenv("GUNICORN_ROLE") == "scheduler":
    scheduler = BackgroundScheduler()
    scheduler.add_job(your_task, 'cron', minute=30)
    scheduler.start()

方案3:定时任务完全脱离服务进程部署

把定时任务逻辑拆成独立的代码片段,用系统crontab、独立的APScheduler进程、Celery Beat等组件单独部署运行,完全不和FastAPI的服务worker耦合,从根源上避免多实例重复执行的问题,适合定时任务逻辑较重的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 11:54:03