跳至內容

背景任務

您可以定義在返回響應*後*執行的背景任務。

這對於在請求後需要執行的操作很有用,但客戶端在接收響應之前並不需要等待操作完成。

這包含,例如:

  • 執行操作後發送的電子郵件通知
    • 由於連接到電子郵件伺服器和發送電子郵件往往「很慢」(幾秒鐘),您可以立即返回響應並在背景中發送電子郵件通知。
  • 處理數據
    • 例如,假設您收到一個必須經過緩慢處理的文件,您可以返回「已接受」(HTTP 202)的響應並在背景中處理該文件。

使用 BackgroundTasks

首先,導入 BackgroundTasks 並在您的*路徑操作函數*中定義一個類型宣告為 BackgroundTasks 的參數。

from fastapi import BackgroundTasks, FastAPI

app = FastAPI()


def write_notification(email: str, message=""):
    with open("log.txt", mode="w") as email_file:
        content = f"notification for {email}: {message}"
        email_file.write(content)


@app.post("/send-notification/{email}")
async def send_notification(email: str, background_tasks: BackgroundTasks):
    background_tasks.add_task(write_notification, email, message="some notification")
    return {"message": "Notification sent in the background"}

**FastAPI** 將會為您創建 BackgroundTasks 類型的物件,並將其作為該參數傳遞。

創建一個任務函數

創建一個作為背景任務運行的函數。

它只是一個可以接收參數的標準函數。

它可以是 async def 或普通的 def 函數,**FastAPI** 將會知道如何正確處理它。

在這種情況下,任務函數將寫入文件(模擬發送電子郵件)。

由於寫入操作不使用 asyncawait,我們使用普通的 def 定義函數。

from fastapi import BackgroundTasks, FastAPI

app = FastAPI()


def write_notification(email: str, message=""):
    with open("log.txt", mode="w") as email_file:
        content = f"notification for {email}: {message}"
        email_file.write(content)


@app.post("/send-notification/{email}")
async def send_notification(email: str, background_tasks: BackgroundTasks):
    background_tasks.add_task(write_notification, email, message="some notification")
    return {"message": "Notification sent in the background"}

添加背景任務

在您的*路徑操作函數*內,使用 .add_task() 方法將您的任務函數傳遞給*背景任務*物件。

from fastapi import BackgroundTasks, FastAPI

app = FastAPI()


def write_notification(email: str, message=""):
    with open("log.txt", mode="w") as email_file:
        content = f"notification for {email}: {message}"
        email_file.write(content)


@app.post("/send-notification/{email}")
async def send_notification(email: str, background_tasks: BackgroundTasks):
    background_tasks.add_task(write_notification, email, message="some notification")
    return {"message": "Notification sent in the background"}

.add_task() 接收以下參數:

  • 要在背景中運行的任務函數 (write_notification)。
  • 應該按順序傳遞給任務函數的任何參數序列 (email)。
  • 應該傳遞給任務函數的任何關鍵字參數 (message="some notification")。

依賴注入

使用 BackgroundTasks 也適用於依賴注入系統,您可以在多個層級宣告類型為 BackgroundTasks 的參數:在*路徑操作函數*中、在依賴項(可依賴項)中、在子依賴項中等等。

**FastAPI** 知道在每種情況下該怎麼做以及如何重用相同的物件,以便所有背景任務都合併在一起並在之後在背景中運行。

from typing import Annotated

from fastapi import BackgroundTasks, Depends, FastAPI

app = FastAPI()


def write_log(message: str):
    with open("log.txt", mode="a") as log:
        log.write(message)


def get_query(background_tasks: BackgroundTasks, q: str | None = None):
    if q:
        message = f"found query: {q}\n"
        background_tasks.add_task(write_log, message)
    return q


@app.post("/send-notification/{email}")
async def send_notification(
    email: str, background_tasks: BackgroundTasks, q: Annotated[str, Depends(get_query)]
):
    message = f"message to {email}\n"
    background_tasks.add_task(write_log, message)
    return {"message": "Message sent"}
from typing import Annotated, Union

from fastapi import BackgroundTasks, Depends, FastAPI

app = FastAPI()


def write_log(message: str):
    with open("log.txt", mode="a") as log:
        log.write(message)


def get_query(background_tasks: BackgroundTasks, q: Union[str, None] = None):
    if q:
        message = f"found query: {q}\n"
        background_tasks.add_task(write_log, message)
    return q


@app.post("/send-notification/{email}")
async def send_notification(
    email: str, background_tasks: BackgroundTasks, q: Annotated[str, Depends(get_query)]
):
    message = f"message to {email}\n"
    background_tasks.add_task(write_log, message)
    return {"message": "Message sent"}
from typing import Union

from fastapi import BackgroundTasks, Depends, FastAPI
from typing_extensions import Annotated

app = FastAPI()


def write_log(message: str):
    with open("log.txt", mode="a") as log:
        log.write(message)


def get_query(background_tasks: BackgroundTasks, q: Union[str, None] = None):
    if q:
        message = f"found query: {q}\n"
        background_tasks.add_task(write_log, message)
    return q


@app.post("/send-notification/{email}")
async def send_notification(
    email: str, background_tasks: BackgroundTasks, q: Annotated[str, Depends(get_query)]
):
    message = f"message to {email}\n"
    background_tasks.add_task(write_log, message)
    return {"message": "Message sent"}

提示

如果可能,請盡量使用 Annotated 版本。

from fastapi import BackgroundTasks, Depends, FastAPI

app = FastAPI()


def write_log(message: str):
    with open("log.txt", mode="a") as log:
        log.write(message)


def get_query(background_tasks: BackgroundTasks, q: str | None = None):
    if q:
        message = f"found query: {q}\n"
        background_tasks.add_task(write_log, message)
    return q


@app.post("/send-notification/{email}")
async def send_notification(
    email: str, background_tasks: BackgroundTasks, q: str = Depends(get_query)
):
    message = f"message to {email}\n"
    background_tasks.add_task(write_log, message)
    return {"message": "Message sent"}

提示

如果可能,請盡量使用 Annotated 版本。

from typing import Union

from fastapi import BackgroundTasks, Depends, FastAPI

app = FastAPI()


def write_log(message: str):
    with open("log.txt", mode="a") as log:
        log.write(message)


def get_query(background_tasks: BackgroundTasks, q: Union[str, None] = None):
    if q:
        message = f"found query: {q}\n"
        background_tasks.add_task(write_log, message)
    return q


@app.post("/send-notification/{email}")
async def send_notification(
    email: str, background_tasks: BackgroundTasks, q: str = Depends(get_query)
):
    message = f"message to {email}\n"
    background_tasks.add_task(write_log, message)
    return {"message": "Message sent"}

在此範例中,訊息將在發送響應*後*寫入 log.txt 文件。

如果請求中存在查詢,它將在背景任務中寫入日誌。

然後,在*路徑操作函數*中生成的另一個背景任務將使用 email 路徑參數寫入訊息。

技術細節

BackgroundTasks 類別直接來自 starlette.background

它被直接導入/包含到 FastAPI 中,以便您可以從 fastapi 導入它,並避免意外地從 starlette.background 導入另一個 BackgroundTask(結尾沒有 s)。

僅使用 BackgroundTasks(而不是 BackgroundTask),就可以將其作為*路徑操作函式*參數,並讓 FastAPI 為您處理其餘部分,就像直接使用 Request 物件一樣。

仍然可以在 FastAPI 中單獨使用 BackgroundTask,但您必須在程式碼中建立物件,並返回包含它的 Starlette Response

您可以在Starlette 的背景任務官方文件中看到更多詳細資訊。

注意事項

如果您需要執行繁重的背景計算,而且不一定需要由同一個程序執行(例如,您不需要共享記憶體、變數等),那麼使用其他更強大的工具(例如 Celery)可能會對您更有利。

它們通常需要更複雜的配置,一個訊息/任務佇列管理器,例如 RabbitMQ 或 Redis,但它們允許您在多個程序中,尤其是在多個伺服器上運行背景任務。

要查看範例,請查看專案產生器,它們都包含已配置好的 Celery。

但是,如果您需要從同一個 FastAPI 應用程式存取變數和物件,或者您需要執行小型背景任務(例如傳送電子郵件通知),則只需使用 BackgroundTasks 即可。

摘要

在*路徑操作函式*和依存項目中導入並使用帶有參數的 BackgroundTasks 以新增背景任務。