FastAPI — 效能與最佳化:uvicorn / gunicorn workers 設定
簡介
在開發 FastAPI 應用時,最常見的部署方式是使用 Uvicorn 或 Gunicorn + 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 shutdown、log 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設為5(2*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 呼叫)。 | 使用 非同步資料庫驅動(asyncpg、databases)或將阻塞操作搬到 背景工作者(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.cpu 與 workers 數量,或使用 autoscaling 動態調整。 |
| preload 造成共享資源衝突 | 預載入模型後,所有 workers 共享同一個記憶體位址,若模型非 thread‑safe 會出錯。 | 確認載入的資源是 只讀,或在每個 worker 中重新建立獨立實例(可在 startup 事件中完成)。 |
最佳實踐
- 先測試單個 worker:使用
ab、hey、wrk等壓測工具,觀測 CPU、記憶體、延遲。 - 逐步加 workers:每次增加 1~2 個,觀察 TPS(每秒請求數)是否線性提升。
- 設定
max-requests:防止長時間運行的 memory leak。 - 使用
--preload:在需要大量共享靜態資源(如模型)時可縮短啟動時間。 - 監控:結合 Prometheus + Grafana 或 Datadog 觀測 workers 的 CPU、記憶體、FD(檔案描述符)使用情況。
實際應用場景
| 場景 | 建議設定 | 為什麼 |
|---|---|---|
| 小型內部工具(單核 VM,使用者 < 10) | uvicorn --workers 1 或 gunicorn -w 2 |
只要能處理低併發,減少資源浪費。 |
| API Gateway(10+ 核心,平均 200 RPS) | workers = (cpu*2)+1,max-requests=2000,timeout=30 |
高併發需要足夠 workers,max‑requests 防止記憶體泄漏。 |
| 機器學習推論服務(載入大型模型 2 GB) | workers = cpu_cores(不加 2),preload + --limit-concurrency 10 |
每個 worker 需要較多記憶體,避免 OOM;限制同時推論數量防止 CPU 飽和。 |
| 容器化微服務(K8s) | resources.limits.cpu: "2000m",env WORKERS=4,autoscaling |
依照 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 服務。祝開發順利,服務持續升級!