背景任務¶
你可以定義背景任務,讓它們在傳回回應之後執行。
這對於那些需要在請求之後發生、但用戶端其實不必在收到回應前等它完成的操作很有用。
例如:
- 在執行某個動作後發送電子郵件通知:
- 由於連線到郵件伺服器並寄送郵件通常較「慢」(數秒),你可以先立即回應,並在背景中發送郵件通知。
- 處理資料:
- 例如,收到一個需要經過較慢處理流程的檔案時,你可以先回應「Accepted」(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 都能正確處理。
在此例中,任務函式會寫入檔案(模擬寄送電子郵件)。
由於寫入操作未使用 async 與 await,因此以一般的 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 型別的參數:路徑操作函式、相依項(dependable)、次級相依項等。
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"}
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
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"}
在此範例中,訊息會在回應送出之後寫入 log.txt 檔案。
如果請求中有查詢參數,會以背景任務寫入日誌。
接著,在路徑操作函式中建立的另一個背景任務會使用 email 路徑參數寫入訊息。
技術細節¶
類別 BackgroundTasks 直接來自 starlette.background。
它被直接匯入/包含到 FastAPI 中,因此你可以從 fastapi 匯入它,並避免不小心從 starlette.background 匯入另一個同名的 BackgroundTask(結尾沒有 s)。
只使用 BackgroundTasks(而非 BackgroundTask)時,你就能把它當作路徑操作函式的參數,並讓 FastAPI 幫你處理其餘部分,就像直接使用 Request 物件一樣。
在 FastAPI 中仍可單獨使用 BackgroundTask,但你需要在程式碼中自行建立該物件,並回傳包含它的 Starlette Response。
更多細節請參閱 Starlette 官方的 Background Tasks 文件。
注意事項¶
如果你需要執行繁重的背景計算,且不一定要由同一個行程執行(例如不需要共用記憶體、變數等),可以考慮使用更大型的工具,例如 Celery。
這類工具通常需要較複雜的設定,以及訊息/工作佇列管理器(如 RabbitMQ 或 Redis),但它們允許你在多個行程,甚至多台伺服器上執行背景任務。
但如果你需要存取同一個 FastAPI 應用中的變數與物件,或只需執行小型的背景任務(例如寄送郵件通知),僅使用 BackgroundTasks 即可。
重點回顧¶
在路徑操作函式與相依項中匯入並使用 BackgroundTasks 參數,以新增背景任務。