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

FastAPI优雅停机时如何终止后台任务?

解决FastAPI --reload模式下优雅关闭的死锁问题

问题根源

当用uvicorn --reload启动FastAPI时,Uvicorn的优雅关闭流程是这样的:

  1. 停止接收新请求
  2. 关闭现有连接
  3. 等待所有后台任务完成
  4. 执行lifespan上下文管理器中yield后的 shutdown 逻辑

你的场景里,后台任务依赖Session.close_all()才能结束,但这个调用又要等后台任务完成后才会执行,直接形成死锁,导致"Shutting down sessions"日志永远无法输出。

可行解决方案

1. 提前注册信号处理器,优先执行会话关闭

在应用启动时注册信号监听,当收到Uvicorn发送的终止信号(SIGTERM)时,先调用Session.close_all()让后台任务正常退出,再继续后续关闭流程。

示例代码:

import asyncio
import signal
from fastapi import FastAPI
from contextlib import asynccontextmanager

class Session:
    _active_sessions = []
    
    @classmethod
    def close_all(cls):
        cls._active_sessions.clear()
        print("All sessions closed")

@asynccontextmanager
async def lifespan(app: FastAPI):
    # 定义信号处理函数
    def handle_shutdown_signal():
        Session.close_all()
    
    # 注册SIGTERM/SIGINT信号处理器
    loop = asyncio.get_running_loop()
    for sig in (signal.SIGTERM, signal.SIGINT):
        loop.add_signal_handler(sig, handle_shutdown_signal)
    
    yield
    # 此时后台任务已结束,shutdown逻辑可正常执行
    print("Shutting down sessions")

app = FastAPI(lifespan=lifespan)

# 示例后台任务
@app.get("/start-task")
async def start_task():
    async def background_task():
        while Session._active_sessions:
            await asyncio.sleep(1)
    asyncio.create_task(background_task())
    return {"status": "task started"}

2. 给后台任务添加退出检测

修改后台任务逻辑,让它定期检查全局关闭标记,当标记触发时主动退出,无需等待Session.close_all()被动结束。

示例代码:

import asyncio
from fastapi import FastAPI
from contextlib import asynccontextmanager

shutdown_flag = asyncio.Event()

class Session:
    _active_sessions = []
    
    @classmethod
    def close_all(cls):
        cls._active_sessions.clear()
        shutdown_flag.set()

@asynccontextmanager
async def lifespan(app: FastAPI):
    yield
    print("Shutting down sessions")
    Session.close_all()

app = FastAPI(lifespan=lifespan)

@app.get("/start-task")
async def start_task():
    async def background_task():
        while not shutdown_flag.is_set():
            await asyncio.sleep(1)
    asyncio.create_task(background_task())
    return {"status": "task started"}

3. 开发环境临时方案

如果只是开发阶段用--reload,可以直接按两次CTRL+C强制关闭,但这仅作为临时手段,生产环境必须使用前面的优雅关闭方案。

注意事项

  • --reload是为开发场景设计的,生产环境建议关闭该选项,直接使用Uvicorn默认的优雅关闭流程
  • 所有后台任务都应设计为可中断的,避免依赖外部触发才能退出

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 06:52:43