我想在 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 {}
Run Code Online (Sandbox Code Playgroud)
解决此问题的最佳方法是什么?
您task的定义为async,这意味着 fastapi(或者更确切地说是 starlette)将在 asyncio 事件循环中运行它。并且因为somelongcomputation是同步的(即不等待某些 IO,而是进行计算),只要它正在运行,它就会阻塞事件循环。
我看到了解决这个问题的几种方法:
使用更多的工人(例如uvicorn main:app --workers 4)。这将允许最多 4 个somelongcomputation并行。
将您的任务重写为不存在async(即,将其定义为def task(data): ...等)。然后starlette 将在一个单独的线程中运行它。
使用fastapi.concurrency.run_in_threadpool,这也将在单独的线程中运行它。像这样:
from fastapi.concurrency import run_in_threadpool
async def task(data):
otherdata = await db.fetch("some sql")
newdata = await run_in_threadpool(lambda: somelongcomputation(data, otherdata))
await db.execute("some sql", newdata)
Run Code Online (Sandbox Code Playgroud)
自己生成一个单独的线程/进程。例如使用concurrent.futures.
使用更严厉的东西,比如芹菜。(这里的 fastapi 文档中也提到了)。