我想在 FastAPI 中运行一个简单的后台任务,这涉及在将其转储到数据库之前进行一些计算。但是,计算会阻止它接收更多请求。
from fastapi import BackgroundTasks, FastAPI
app = FastAPI()
db = Database()
async def task(data):
otherdata = await db.fetch("some sql")
newdata = somelongcomputation(data,otherdata) # this blocks other requests
await db.execute("some sql",newdata)
@app.post("/profile")
async def profile(data: Data, background_tasks: BackgroundTasks):
background_tasks.add_task(task, data)
return {}
解决此问题的最佳方法是什么?
原文由 Gary Ong 发布,翻译遵循 CC BY-SA 4.0 许可协议
您的
task
定义为async
,这意味着 fastapi(或者更确切地说是 starlette)将在 asyncio 事件循环中运行它。并且因为somelongcomputation
是同步的(即不等待某些 IO,而是进行计算)只要它正在运行,它就会阻塞事件循环。我看到了解决这个问题的几种方法:
使用更多的工人(例如
uvicorn main:app --workers 4
)。这将允许最多 4 个somelongcomputation
并行。将您的任务重写为不是
async
(即将其定义为def task(data): ...
等)。然后 starlette 将在单独的线程中运行它。使用
fastapi.concurrency.run_in_threadpool
,这也将在单独的线程中运行它。像这样:或者直接使用
asyncios
的run_in_executor
(其中run_in_threadpool
在后台使用):您甚至可以传入
concurrent.futures.ProcessPoolExecutor
作为run_in_executor
的第一个参数,以在单独的进程中运行它。自己生成一个单独的线程/进程。例如使用
concurrent.futures
。使用更重的东西,如芹菜。 (也在 此处 的 fastapi 文档中提到)。