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

FastAPI多Worker环境下如何避免APScheduler任务重复执行?

解决FastAPI多Worker下APScheduler任务重复执行的问题

当使用Uvicorn启动多Worker时,每个Worker都会独立加载FastAPI应用代码,导致APScheduler被多次初始化并启动,任务重复执行。以下是基于Python标准库的最简解决方案,无需额外依赖第三方模块:

实现思路

通过跨平台文件锁实现单实例调度器:只有第一个成功获取文件锁的Worker进程会启动APScheduler,其他进程因无法获取锁而跳过调度器启动步骤,从而保证任务仅在单个进程中执行。

修改后的代码

from apscheduler.schedulers.asyncio import AsyncIOScheduler
from fastapi import FastAPI
import os
import sys
from contextlib import contextmanager

@contextmanager
def single_instance_lock(lock_file="/tmp/apscheduler_single_instance.lock"):
    lock_fd = None
    try:
        # 创建/打开锁文件,仅写模式,不存在则创建
        lock_fd = os.open(lock_file, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o644)
        
        # 尝试获取排他锁(非阻塞)
        if sys.platform == 'win32':
            import msvcrt
            msvcrt.locking(lock_fd, msvcrt.LK_NBLCK, 1)
        else:
            import fcntl
            fcntl.flock(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
        
        yield
    except (BlockingIOError, PermissionError):
        # 无法获取锁,说明已有进程启动调度器,直接跳过
        pass
    finally:
        if lock_fd is not None:
            os.close(lock_fd)

def some_job():
    print(f"hello world from process {os.getpid()}")

app = FastAPI()
shed = AsyncIOScheduler()
shed.add_job(some_job, "interval", seconds=5)

@app.get("/test")
async def test():
    return {"Hello": "world"}

# 仅在获取到锁的进程中启动调度器
with single_instance_lock():
    shed.start()

代码说明

  1. 文件锁上下文管理器:single_instance_lock函数实现了跨平台的非阻塞排他锁,保证同一时间只有一个进程能获取锁。
  2. 调度器启动控制:用with语句包裹shed.start(),只有成功获取锁的进程会执行调度器启动逻辑,其他进程自动跳过。
  3. 自动锁释放:当进程退出时,文件描述符会自动关闭,锁也随之释放,避免遗留无效锁。

验证效果

启动多Worker后执行:

uvicorn main:app --workers 4

此时任务仅会每隔5秒打印一次hello world from process [PID],而非4次,解决了重复执行问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 12:25:37