我想在 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 {}
解决这个问题的最佳方法是什么?
Your task
定义为async
,这意味着 fastapi (或更确切地说 starlette)将在 asyncio 事件循环中运行它。
并且因为somelongcomputation
是同步的(即不等待某些 IO,而是进行计算),只要它正在运行,它就会阻塞事件循环。
我看到了解决这个问题的几种方法:
-
使用更多的工人(例如uvicorn main:app --workers 4
)。这将允许最多 4somelongcomputation
在平行下。
-
重写你的任务不async
(即将其定义为def task(data): ...
ETC)。然后starlette将在单独的线程中运行它。
-
Use fastapi.concurrency.run_in_threadpool https://github.com/tiangolo/fastapi/issues/1066#issuecomment-612940187,这也会在单独的线程中运行它。就像这样:
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)
- Or use asyncios's run_in_executor https://docs.python.org/3/library/asyncio-eventloop.html#asyncio.loop.run_in_executor直接(其中
run_in_threadpool
在引擎盖下使用):import asyncio
async def task(data):
otherdata = await db.fetch("some sql")
loop = asyncio.get_running_loop()
newdata = await loop.run_in_executor(None, lambda: somelongcomputation(data, otherdata))
await db.execute("some sql", newdata)
你甚至可以传递一个concurrent.futures.ProcessPoolExecutor https://docs.python.org/3/library/concurrent.futures.html?highlight=concurrent%20futures%20processpoolexecutor#concurrent.futures.ProcessPoolExecutor作为第一个参数run_in_executor
在单独的进程中运行它。
-
自己生成一个单独的线程/进程。例如。使用concurrent.futures https://docs.python.org/3/library/concurrent.futures.html#threadpoolexecutor-example.
-
使用更重的东西,比如芹菜。 (fastapi 文档中也提到here https://fastapi.tiangolo.com/tutorial/background-tasks/#caveat).
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)