本文 AI 產出,尚未審核

FastAPI — 效能與最佳化:uvicorn / gunicorn workers 設定


簡介

在開發 FastAPI 應用時,最常見的部署方式是使用 UvicornGunicorn + Uvicorn workers。這兩個 ASGI 伺服器本身已經非常輕量、效能高,但若沒有正確設定 workers(工作執行緒/進程),仍可能成為瓶頸。

  • workers 數量 直接影響 CPU 核心的利用率與同時處理請求的能力。
  • 不當的設定會導致 CPU 飽和、記憶體耗盡,甚至是 請求延遲

本單元將說明什麼是 workers、如何根據硬體與應用特性調校,並提供實作範例與最佳實踐,幫助你把 FastAPI 部署到生產環境時,達到最佳效能。


核心概念

1. 為什麼需要多個 workers

Uvicorn 本身是 單執行緒、單進程 的非同步伺服器,對於 I/O 密集(例如資料庫、外部 API)相當友善。但在 CPU 密集(例如大量資料處理、加密)或 高併發 場景下,單一進程無法充分利用多核心 CPU。

  • workers = 同時運行的 Uvicorn 進程(或執行緒),每個 worker 都擁有自己的事件迴圈與資源。
  • 多 workers = 平行 處理多個請求,提升吞吐量(TPS)與穩定性。

Tip:在容器化(Docker/K8s)環境下,建議 將 workers 數量設定為 CPU 核心數的 2~4 倍,但要根據記憶體限制做調整。


2. Uvicorn vs Gunicorn + Uvicorn workers

項目 Uvicorn 單體 Gunicorn + Uvicorn workers
啟動方式 uvicorn app:app gunicorn -k uvicorn.workers.UvicornWorker app:app
多進程支援 需要外部工具(如 --workers 內建 workers 機制
監控/熱重載 內建 --reload(開發用) 支援 --reload--preload--timeout 等參數
生產環境建議 小型或單核服務 中大型、需要水平擴展的服務

Gunicorn 本身是 WSGI 伺服器,但透過 uvicorn.workers.UvicornWorker 可以將 ASGI 應用交給 Uvicorn 處理,同時享受 Gunicorn 的 進程管理Graceful shutdownlog rotation 等功能。


3. 計算適當的 workers 數量

最常見的公式是:

workers = (cpu_cores * 2) + 1
  • cpu_cores:容器或實體機的 CPU 核心數。
  • +1:保留一個額外的 worker 以處理突發流量。

範例:若伺服器有 4 核心,則建議 workers = 9。不過,如果每個 worker 的記憶體需求較大(例如載入大型模型),就必須 降低 workers,以免 OOM(Out Of Memory)。


4. 重要參數說明

參數 說明 常見值
--workers / -w 設定 workers 數量 $(($(nproc) * 2 + 1))
--threads 每個 worker 的執行緒數(僅在 --worker-class=gthread 時有效) 2~4
--timeout worker 無回應的最大秒數,超過會被重啟 30
--max-requests 每個 worker 處理的請求上限,達到上限後自動重啟(防止記憶體泄漏) 1000
--preload 在 master process 中預先載入應用程式,減少 worker 啟動時間 --preload

程式碼範例

以下示範 3 種不同情境 的部署指令與 Dockerfile 配置,均附上說明註解。

範例 1️⃣:單機開發(Uvicorn + reload)

# 直接在本機執行,適合開發階段
uvicorn app.main:app \
    --host 0.0.0.0 \
    --port 8000 \
    --reload          # 變更程式碼自動重載
  • --reload 只建議在開發環境使用,會額外消耗 CPU。

範例 2️⃣:生產環境(Gunicorn + Uvicorn workers)

# 假設機器有 4 核心,使用 (4*2)+1=9 個 workers
gunicorn app.main:app \
    -k uvicorn.workers.UvicornWorker \
    -w 9 \
    --bind 0.0.0.0:8000 \
    --log-level info \
    --timeout 30 \
    --max-requests 1000 \
    --preload
  • -k uvicorn.workers.UvicornWorker 告訴 Gunicorn 使用 Uvicorn 作為 worker。
  • --preload 讓 master 先載入應用,減少每個 worker 的啟動時間。

範例 3️⃣:Docker 部署(多 workers)

# Dockerfile
FROM python:3.11-slim

# 安裝依賴
RUN pip install --no-cache-dir fastapi uvicorn gunicorn

# 複製程式碼
COPY ./app /app
WORKDIR /app

# 設定環境變數(可在 CI/CD 中覆寫)
ENV WORKERS=9
ENV BIND=0.0.0.0:8000

# 使用 entrypoint 讓 workers 數量可動態調整
ENTRYPOINT ["gunicorn", "app.main:app", "-k", "uvicorn.workers.UvicornWorker"]
CMD ["-w", "${WORKERS}", "--bind", "${BIND}", "--log-level", "info"]

說明

  • ENV WORKERS=9 讓使用者在 docker run -e WORKERS=5 時可自訂。
  • CMD 中的 ${WORKERS} 會在容器啟動時展開。

範例 4️⃣:Kubernetes Deployment(自動調整)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: fastapi-prod
spec:
  replicas: 3               # Pod 數量(水平擴展)
  selector:
    matchLabels:
      app: fastapi
  template:
    metadata:
      labels:
        app: fastapi
    spec:
      containers:
      - name: fastapi
        image: myrepo/fastapi:latest
        env:
        - name: WORKERS
          value: "6"        # 每個 Pod 內的 workers 數量
        ports:
        - containerPort: 8000
        resources:
          limits:
            cpu: "2000m"    # 2 CPU cores
            memory: "1Gi"
  • 在 Kubernetes 中,Pod 內的 cpu 限制會影響 workers 的上限。
  • cpu 限制是 2 核,建議 workers 設為 52*2+1)。

範例 5️⃣:使用 uvicorn--workers(僅在 0.24+ 版本)

# uvicorn 0.24 之後支援內建 workers
uvicorn app.main:app \
    --host 0.0.0.0 \
    --port 8000 \
    --workers 4 \
    --log-level warning
  • 直接透過 uvicorn 設定 workers,適合不想額外安裝 Gunicorn 的情況。
  • 仍建議在 生產 環境使用 Gunicorn,因其提供更完整的監控與 graceful shutdown 機制。

常見陷阱與最佳實踐

陷阱 為何會發生 解決方式
記憶體 OOM 每個 worker 都會載入完整的依賴(模型、資料庫連線池),過多 workers 會耗盡記憶體。 測量單個 worker 的記憶體使用,再根據容器/實體機的限制計算最大可容納的 workers。
CPU 飽和卻無法提升效能 只增加 workers 而不檢查 I/O 阻塞(例如同步 DB 呼叫)。 使用 非同步資料庫驅動asyncpgdatabases)或將阻塞操作搬到 背景工作者(Celery、RQ)。
Graceful shutdown 失效 使用 uvicorn --reload 或未設定 --timeout,導致關閉時直接 kill。 在生產環境 關閉 reload,並設定 --timeout--graceful-timeout(Gunicorn)以確保請求完成。
worker 數量與容器 CPU 配額不符 在 Kubernetes 中未設定 resources.limits.cpu,導致容器實際只得到 0.5 核心,但設定了 4 workers。 同步 resources.limits.cpuworkers 數量,或使用 autoscaling 動態調整。
preload 造成共享資源衝突 預載入模型後,所有 workers 共享同一個記憶體位址,若模型非 thread‑safe 會出錯。 確認載入的資源是 只讀,或在每個 worker 中重新建立獨立實例(可在 startup 事件中完成)。

最佳實踐

  1. 先測試單個 worker:使用 abheywrk 等壓測工具,觀測 CPU、記憶體、延遲。
  2. 逐步加 workers:每次增加 1~2 個,觀察 TPS(每秒請求數)是否線性提升。
  3. 設定 max-requests:防止長時間運行的 memory leak。
  4. 使用 --preload:在需要大量共享靜態資源(如模型)時可縮短啟動時間。
  5. 監控:結合 Prometheus + GrafanaDatadog 觀測 workers 的 CPU、記憶體、FD(檔案描述符)使用情況。

實際應用場景

場景 建議設定 為什麼
小型內部工具(單核 VM,使用者 < 10) uvicorn --workers 1gunicorn -w 2 只要能處理低併發,減少資源浪費。
API Gateway(10+ 核心,平均 200 RPS) workers = (cpu*2)+1max-requests=2000timeout=30 高併發需要足夠 workers,max‑requests 防止記憶體泄漏。
機器學習推論服務(載入大型模型 2 GB) workers = cpu_cores(不加 2),preload + --limit-concurrency 10 每個 worker 需要較多記憶體,避免 OOM;限制同時推論數量防止 CPU 飽和。
容器化微服務(K8s) resources.limits.cpu: "2000m"env WORKERS=4autoscaling 依照 Pod 的 CPU 限制調整 workers,配合 HPA 伸縮。
長連線 WebSocket(即時聊天) workers = cpu_cores--limit-concurrency 100--ws ping-interval 20 WebSocket 需要保持連線,過多 workers 會增加 context switching。

總結

  • workers 是提升 FastAPI 在 CPU 多核心 環境下吞吐量的關鍵設定。
  • Uvicorn 單體適合開發或極小服務;Gunicorn + Uvicorn workers 才是生產環境的首選,因其提供進程管理、Graceful shutdown、log rotation 等功能。
  • 計算 workers 時,可先用 (cpu*2)+1 作為上限,然後根據 記憶體需求、I/O 性質 再微調。
  • 透過 --preload--max-requests--timeout 等參數,能讓服務在長時間運行下保持穩定。
  • 最後,監控壓測 是不可或缺的步驟:只有在實測數據的支持下,才能確定設定是否最適。

掌握了 workers 的調校技巧,你的 FastAPI 應用就能在 雲端、容器、甚至裸機 上發揮出最佳效能,為使用者提供快速、可靠的 API 服務。祝開發順利,服務持續升級!