fastapi 异步后台任务会阻止其他请求吗?

Gar*_*Ong 4 python fastapi

我想在 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)

解决此问题的最佳方法是什么?

mih*_*ihi 6

您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 文档中也提到了)。

  • 我在这里面临同样的问题,我想知道为什么不只使用 `asyncio.create_task(task(data))` ?我正在做一些测试,似乎是解决方案。 (3认同)
  • 我认为线程在这里没有任何帮助。由于 Python 不利用真正的并行性,因此为繁重的 CPU 计算生成线程仍将保留整个程序。请改用进程。 (2认同)