FastAPI中BackgroundTasks异步DB操作引发RuntimeError问题求助
问题解决:FastAPI后台任务与GET查询的事件循环冲突
核心问题原因
- 事件循环不统一:你在
run_task里手动创建了新的asyncio事件循环,导致Tortoise数据库连接池绑定到这个私有的后台循环上;而GET请求在FastAPI的主事件循环中执行查询,调用的连接池属于另一个循环,引发different loop错误。 - GET路由逻辑错误:当前GET路由的函数名与POST路由重复,且自行生成
task_id,无法正确查询用户提交的任务状态。 - 重复初始化Tortoise:每次执行
long_time_task都重新初始化Tortoise,不仅浪费资源,还会加剧循环绑定问题。
分步解决方案
1. 修正GET路由的参数与逻辑
将GET路由改为接收用户传入的task_id参数,同时修正函数名避免冲突:
@router.get("/status") async def get_task_status(task_id: str): task_record = await MyModel.filter(task_id=task_id).first() if not task_record: return {'code': 404, 'message': '任务不存在'} return {'code': 200, 'status': task_record.status}
2. 统一事件循环,移除手动创建的循环
FastAPI的BackgroundTasks支持直接添加异步任务,无需手动创建新循环。修改run_task为异步函数,直接复用主循环:
async def run_task(task_id): await long_time_task(task_id)
然后在POST路由中直接添加该异步任务:
@router.post("/upload") async def upload(background_tasks: BackgroundTasks): task_id = str(uuid.uuid1()) background_tasks.add_task(run_task, task_id) return {'code': 200, 'task_id': task_id, 'message': '任务已启动'}
3. 全局初始化Tortoise数据库
在FastAPI启动时全局初始化Tortoise,避免每次任务重复初始化:
from fastapi import FastAPI from tortoise import Tortoise from dbs.database import TORTOISE_ORM app = FastAPI() # 启动时初始化数据库 @app.on_event("startup") async def startup_event(): await Tortoise.init(config=TORTOISE_ORM) await Tortoise.generate_schemas() # 关闭时释放数据库连接 @app.on_event("shutdown") async def shutdown_event(): await Tortoise.close_connections()
同时简化long_time_task,移除重复初始化代码:
import asyncio async def long_time_task(task_id): # 创建任务初始记录 await MyModel.create(task_id=task_id, status="running") # 模拟耗时业务操作 await asyncio.sleep(10) # 任务完成后更新状态 await MyModel.filter(task_id=task_id).update(status="completed")
4. 额外建议:完善任务状态字段
确保MyModel包含明确的状态字段(如status,可设置默认值为pending),让GET请求能返回准确的任务进度。
内容的提问来源于stack exchange,提问作者haojie
相关产品推荐
相关产品推荐

