FastAPI后台任务阻塞:新请求需等待旧请求完成后才响应
FastAPI后台长任务阻塞请求的解决方法
问题场景
我尝试在后台执行长任务the_longer_function,期望API将任务加入后台后立即返回响应,无需等待任务完成。但实际观察到,当前置请求未完成时,新请求会被阻塞,必须等前置请求完成才返回响应。试过async、trio、并行和并发等方式都未解决。
现有代码
路由处理代码
@app.post("/something/something_test", response_class=JSONResponse) @validate_token async def home(request: Request, background_tasks: BackgroundTasks): try: request_params = await request.json() background_tasks.add_task(the_longer_function, request_params) return JSONResponse({ "Result": "Execution Started!" }, status_code=200) except Exception as ex: return { "Result": f"Error in starting execution. Error {ex}" }
长任务函数
def the_longer_function(request_params): variable = None try: variable = request_params.get('variable', None) executionId = str(uuid.uuid4()) bot_message['ExecutionId'] = executionId """ 耗时业务逻辑 """ publish_bot_scan_data(request_params, variable) except (JSONDecodeError, Exception) as ex: log.error(f"Error {ex}")
问题原因
FastAPI的BackgroundTasks默认在事件循环线程中执行任务,而the_longer_function是同步阻塞函数,其中的耗时业务逻辑会彻底卡住事件循环,导致后续请求无法被处理,必须等待当前任务执行完毕。
解决方案
方案1:用asyncio.to_thread将同步任务移至线程池
将同步任务放到独立线程执行,避免阻塞事件循环,修改路由中的任务添加逻辑即可:
import asyncio @app.post("/something/something_test", response_class=JSONResponse) @validate_token async def home(request: Request, background_tasks: BackgroundTasks): try: request_params = await request.json() # 用asyncio.to_thread把同步任务委托给线程池 background_tasks.add_task(asyncio.to_thread, the_longer_function, request_params) return JSONResponse({ "Result": "Execution Started!" }, status_code=200) except Exception as ex: return JSONResponse({ "Result": f"Error in starting execution. Error {ex}" }, status_code=500)
注意:如果the_longer_function中使用了全局变量(如bot_message),需要处理线程安全问题,建议通过参数传递替代全局变量,或添加线程锁。
方案2:使用独立任务队列(生产环境推荐)
对于超长时间运行的任务,建议使用Celery、RQ等任务队列框架,结合Redis或RabbitMQ作为消息中间件,将任务完全脱离FastAPI进程执行。这种方式能彻底避免任务对API服务的影响,还支持任务监控、重试等功能。
内容的提问来源于stack exchange,提问作者SalGorithm
相关产品推荐
相关产品推荐

