FastAPI Lifespan Events:管理應用程式啟動與關閉的生命週期

在開發 Web 應用程式時,我們經常需要在程式 「啟動前」 執行一些初始化操作(例如:建立資料庫連線池、載入機器學習模型),並在程式 「關閉時」 執行資源釋放(例如:清理快取、關閉連線)。

FastAPI 提供了一個現代化且簡單的方式來處理這些需求:Lifespan Events

什麼是 Lifespan?

Lifespan 指的是應用程式從「開始運行」到「結束運行」的整個週期。

在早期的 FastAPI 版本中,我們使用的是 startupshutdown 事件處理器。雖然它們依然可用,但現在更推薦使用 lifespan 參數,因為它基於 Python 的 非同步上下文管理器 (Async Context Manager),能更優雅地處理資源的生命週期,且支援跨多個版本的一致性。

如何使用 Lifespan?

我們需要定義一個使用 @asynccontextmanager 裝飾的非同步函式,並將其傳遞給 FastAPI 實例的 lifespan 參數。

基本範例

from contextlib import asynccontextmanager
from fastapi import FastAPI

@asynccontextmanager
async def lifespan(app: FastAPI):
    # --- 這裡寫程式啟動前的邏輯 ---
    print("應用程式正在啟動,準備初始化資源...")
    db_connection = "Connected" # 模擬資源初始化

    yield  # 程式會在這裡暫停,開始接收 Request 的處理流程

    # --- 這裡寫程式關閉前的邏輯 ---
    print("應用程式正在關閉,釋放資源中...")
    db_connection = "Disconnected"
    print(f"資料庫狀態: {db_connection}")

app = FastAPI(lifespan=lifespan)

@app.get("/")
async def read_root():
    return {"message": "Hello World"}
  1. yield 之前:在應用程式開始處理任何 Request 之前執行。
  2. yield 之後:在應用程式完全處理完請求並準備關閉時執行。

實戰應用:管理資料庫連線池

在生產環境中,頻繁地開啟和關閉資料庫連線效能很差。使用 Lifespan 可以讓我們在啟動時建立一個全域的連線池。

from contextlib import asynccontextmanager
from fastapi import FastAPI

# 假設這是一個虛擬的資料庫套件
class Database:
    async def connect(self):
        print("連線到資料庫池...")
    async def disconnect(self):
        print("關閉資料庫池...")

db = Database()

@asynccontextmanager
async def lifespan(app: FastAPI):
    # 啟動時連線
    await db.connect()

    yield {"db": db} # 也可以將資源傳遞給 app.state (選用)

    # 關閉時斷開
    await db.disconnect()

app = FastAPI(lifespan=lifespan)

載入大型資源 (如 ML 模型)

如果你正在開發一個處理圖片辨識或 NLP 的 API,載入模型可能需要好幾秒鐘。將其放在 Lifespan 中可以避免第一個 User 請求時才在載入的延遲感。

import torch # 範例
from contextlib import asynccontextmanager
from fastapi import FastAPI

ml_models = {}

@asynccontextmanager
async def lifespan(app: FastAPI):
    # 啟動時載入重量級模型到記憶體(或 GPU)
    print("Loading ML models...")
    ml_models["sentiment_analysis"] = torch.load("model.pth")

    yield

    # 關閉時清理
    ml_models.clear()

app = FastAPI(lifespan=lifespan)

為什麼 Lifespan 比 Startup/Shutdown 好?

  1. 結構清晰:初始化與清理邏輯寫在同一個函式中,容易閱讀且不容易漏掉清理步驟。
  2. 異常處理:可以使用標準的 try...finally... 來確保資源即使發生錯誤也會被釋放。
  3. 效能:基於 anyioasyncio 的底層優化,執行更高效。
如果你還在使用舊專案中的 @app.on_event("startup"),雖然它目前還能跑,但官方強烈建議未來轉換到 lifespan 方式,因為舊有的 on_event 已經被官方標註為 Deprecated(棄用)。