01-7. Worker 如何選擇
⏱️ 閱讀時間: 8 分鐘 🎯 難度: ⭐⭐⭐ (重要!面試必考)
🎯 本篇重點
這是基礎篇最重要的一篇!學會如何根據應用特性選擇正確的 Worker 類型。
⚠️ 新專案?先看這裡!
如果你正在開始一個新的 Django 專案(Django 3.0+),強烈建議直接使用現代的 ASGI 方案:
# 現代推薦方案(新專案)
pip install uvicorn[standard]
# 開發環境
uvicorn myproject.asgi:application --reload
# 生產環境
uvicorn myproject.asgi:application --workers 4 --host 0.0.0.0 --port 8000為什麼?
- ✅ Python 官方異步標準(asyncio)
- ✅ Django 3.0+ 原生支持 async views
- ✅ 更好的生態系統和長期支持
- ✅ 不需要 Monkey Patching
- ✅ 更清晰的 async/await 語法
本篇討論的 Gunicorn Workers 主要適用於:
- 維護舊的 WSGI Django 專案(Django < 3.0)
- 暫時無法遷移到 ASGI 的專案
- 混合 WSGI/ASGI 的過渡期
📊 四種方案完整對比
核心特性對比(含現代方案)
| 特性 | ASGI (Uvicorn) | Gevent | Gthread | Sync |
|---|---|---|---|---|
| 技術狀態 | 🆕 現代推薦 | ⚠️ 舊專案維護 | ⚠️ 過渡方案 | ✅ 穩定可靠 |
| Django 支持 | 3.0+ 原生 | WSGI only | WSGI only | WSGI 標準 |
| 並發模型 | 進程 + asyncio | 進程 + 協程 | 進程 + 線程 | 進程 |
| 單 Worker 並發 | 數千+ | 1000+ | threads 數 | 1 |
| 語法 | async/await | 同步風格 | 同步 | 同步 |
| Monkey Patch | ❌ 不需要 | ⚠️ 必須 | ❌ 不需要 | ❌ 不需要 |
| CPU 密集型 | ⚠️ 可用 | ❌ 差 | ⚠️ 受 GIL 限制 | ✅ 優秀 |
| I/O 密集型 | ✅ 優秀 | ✅ 優秀 | ✅ 良好 | ❌ 浪費 |
| 混合型任務 | ✅ 優秀 | ⚠️ 可用 | ✅ 優秀 | ⚠️ 可用 |
| 長期維護 | ✅ 推薦 | ⚠️ 技術債務 | ✅ 可接受 | ✅ 穩定 |
| 複雜度 | ⭐⭐⭐ 需學習 async | ⭐⭐⭐ 複雜 | ⭐⭐ 中等 | ⭐ 簡單 |
| 除錯難度 | ⭐⭐⭐ 中等 | ⭐⭐⭐⭐ 困難 | ⭐⭐ 中等 | ⭐ 容易 |
效能對比(實測數據)
測試場景:1000 個請求
1. 快速請求(< 10ms)
├─ Sync: 2500 req/s ⭐⭐⭐⭐⭐
├─ Gthread: 2200 req/s ⭐⭐⭐⭐
└─ Gevent: 2000 req/s ⭐⭐⭐⭐
2. I/O 密集(外部 API,1 秒)
├─ Sync: 4 req/s ⭐
├─ Gthread: 40 req/s ⭐⭐⭐
└─ Gevent: 500 req/s ⭐⭐⭐⭐⭐
3. CPU 密集(複雜計算)
├─ Sync: 100 req/s ⭐⭐⭐⭐⭐
├─ Gthread: 80 req/s ⭐⭐⭐
└─ Gevent: 50 req/s ⭐⭐
4. 混合型(I/O + CPU)
├─ Sync: 26 req/s ⭐⭐
├─ Gthread: 106 req/s ⭐⭐⭐⭐⭐
└─ Gevent: 142 req/s ⭐⭐⭐⭐🌳 決策樹
完整決策流程(2025 年版)
開始
│
├─ 這是新專案嗎?(Django 3.0+)
│ │
│ ├─ YES → 直接用 ASGI (Uvicorn) ✅ 🆕
│ │ pip install uvicorn[standard]
│ │ uvicorn myproject.asgi:application --workers 4
│ │
│ │ 理由:
│ │ • Python 官方標準
│ │ • Django 原生支持 async views
│ │ • 更好的長期維護
│ │
│ └─ NO(舊專案/WSGI)→ 繼續下方決策
│
├─ 應用主要做什麼?
│
├─ CPU 密集(大量計算)?
│ └─ YES → 用 Sync Worker ✅
│ 配置:workers = CPU_cores
│
├─ I/O 密集(大量等待)?
│ │
│ ├─ 並發需求 > 500 且無法遷移到 ASGI?
│ │ └─ YES → 用 Gevent Worker ⚠️ (考慮遷移到 ASGI)
│ │ 配置:workers = CPU × 2 + 1
│ │ worker-connections = 1000
│ │
│ └─ 並發需求 < 500?
│ └─ 用 Gthread Worker ✅
│ 配置:workers = CPU × 2 + 1
│ threads = 2-4
│
└─ 混合型(I/O + CPU)?
└─ 用 Gthread Worker ✅
配置:workers = CPU × 2 + 1
threads = 4🎯 按場景選擇
場景 1:內容管理系統(CMS)
# 特點:
# - CRUD 操作為主
# - 資料庫查詢多
# - 並發不高(< 100)
# - 請求快速(< 100ms)
# 範例:
def create_post(request):
post = Post.objects.create(
title=request.POST['title'],
content=request.POST['content'],
author=request.user
)
return JsonResponse({'id': post.id})推薦:Sync Worker
gunicorn myproject.wsgi:application \
--workers 9 \
--worker-class sync \
--timeout 30理由:
- ✅ 請求快速,不浪費資源
- ✅ 簡單可靠
- ✅ 並發需求低
- ✅ 除錯容易
場景 2:API 聚合服務
# 特點:
# - 呼叫多個外部 API
# - 大量 I/O 等待
# - 並發高(> 500)
# - 每個請求 1-3 秒
# 範例:
def dashboard(request):
# 呼叫 5 個外部 API
weather = requests.get('https://api.weather.com')
news = requests.get('https://api.news.com')
stocks = requests.get('https://api.stocks.com')
social = requests.get('https://api.twitter.com')
analytics = requests.get('https://api.analytics.com')
return JsonResponse({...})推薦:Gevent Worker
pip install gevent
gunicorn myproject.wsgi:application \
--workers 4 \
--worker-class gevent \
--worker-connections 1000 \
--timeout 60# gunicorn_config.py
import gevent.monkey
gevent.monkey.patch_all()理由:
- ✅ 大量 I/O 等待,協程最適合
- ✅ 高並發(4 × 1000 = 4000)
- ✅ 充分利用等待時間
- ✅ 記憶體效率高
場景 3:電商網站
# 特點:
# - 資料庫查詢 + 業務邏輯計算
# - 中等並發(100-500)
# - 混合型任務
# - 需要穩定可靠
# 範例:
def checkout(request):
# I/O:查詢資料庫
cart = Cart.objects.get(user=request.user)
products = cart.products.all()
# CPU:計算折扣
discount = calculate_discount(products, request.user)
# CPU:計算運費
shipping = calculate_shipping(cart.address)
# I/O:創建訂單
order = Order.objects.create(...)
# I/O:發送通知
send_order_email(order)
return JsonResponse({'order_id': order.id})推薦:Gthread Worker
gunicorn myproject.wsgi:application \
--workers 9 \
--worker-class gthread \
--threads 4 \
--timeout 30理由:
- ✅ 平衡 I/O 和 CPU
- ✅ 並發數適中(9 × 4 = 36)
- ✅ 不需要 Monkey Patch
- ✅ 穩定可靠
場景 4:數據分析平台
# 特點:
# - 大量 CPU 計算
# - 處理大數據集
# - 並發低(< 50)
# - 每個請求耗時長
# 範例:
def analyze_data(request):
file = request.FILES['data']
df = pd.read_csv(file)
# 複雜的數據分析
result = df.groupby('category').agg({
'sales': ['sum', 'mean', 'std'],
'profit': ['sum', 'mean']
})
# 生成報告
report = generate_report(result)
return JsonResponse(report)推薦:Sync Worker
gunicorn myproject.wsgi:application \
--workers 4 \
--worker-class sync \
--timeout 300 \
--max-requests 100理由:
- ✅ CPU 密集,多進程可並行
- ✅ Workers = CPU 核心數
- ✅ 不受 GIL 限制
- ✅ 簡單可靠
場景 5:即時聊天系統
# 特點:
# - WebSocket 長連接
# - 超高並發(> 1000)
# - 主要是等待和轉發消息
# - 需要保持連接
# 範例:
def chat_stream(request):
def event_stream():
while True:
message = get_new_message(request.user)
if message:
yield f"data: {message}\n\n"
gevent.sleep(0.1)
return StreamingHttpResponse(event_stream())推薦:Gevent Worker + Django Channels
pip install gevent channels
gunicorn myproject.wsgi:application \
--workers 4 \
--worker-class gevent \
--worker-connections 2000 \
--timeout 0理由:
- ✅ 長連接,協程最適合
- ✅ 超高並發(4 × 2000 = 8000)
- ✅ 連接保持,不佔用資源
- ✅ 記憶體效率極高
場景 6:爬蟲服務
# 特點:
# - 批量抓取網頁
# - 大量網絡請求
# - 並發極高(> 1000)
# - I/O 密集
# 範例:
def scrape_websites(request):
urls = request.POST.getlist('urls') # 100+ URLs
import gevent
from gevent import monkey
monkey.patch_all()
jobs = [gevent.spawn(fetch_url, url) for url in urls]
gevent.joinall(jobs)
results = [job.value for job in jobs]
return JsonResponse({'results': results})推薦:Gevent Worker
gunicorn myproject.wsgi:application \
--workers 8 \
--worker-class gevent \
--worker-connections 2000 \
--timeout 300理由:
- ✅ 極高並發(8 × 2000 = 16000)
- ✅ 網絡請求密集
- ✅ 充分利用等待時間
- ✅ 記憶體占用低
場景 7:微服務 API
# 特點:
# - RESTful API
# - 服務間 RPC 呼叫
# - 中等並發(200-500)
# - 混合 I/O 和計算
# 範例:
def user_service(request):
# RPC 呼叫其他服務
orders = requests.get(f'{ORDER_SERVICE}/orders?user={user_id}')
payments = requests.get(f'{PAYMENT_SERVICE}/history?user={user_id}')
# 計算統計
stats = calculate_user_stats(orders, payments)
return JsonResponse(stats)推薦:Gthread Worker
gunicorn myproject.wsgi:application \
--workers 9 \
--worker-class gthread \
--threads 4 \
--timeout 60理由:
- ✅ 平衡並發能力
- ✅ 混合型任務
- ✅ 穩定可靠
- ✅ 適合微服務架構
📋 快速選擇表
首要決策:新專案 vs 舊專案
| 專案類型 | Django 版本 | 推薦方案 | 理由 |
|---|---|---|---|
| 新專案 | Django 3.0+ | ASGI (Uvicorn) 🆕 | Python 官方標準,長期支持 |
| 舊專案 | Django < 3.0 | Gunicorn Workers | 需要逐步遷移到 ASGI |
| 過渡期 | Django 3.0+ | Gthread → ASGI | 平滑遷移路徑 |
按任務類型選擇(舊專案/WSGI)
| 任務類型 | 特徵 | 推薦方案 | 配置 |
|---|---|---|---|
| 新專案(任何類型) | Django 3.0+ | ASGI (Uvicorn) | workers = 4 |
| CPU 密集 | 計算、數據分析 | Sync | workers = CPU |
| I/O 密集(低並發) | API 呼叫、< 500 並發 | Gthread | workers × threads |
| I/O 密集(高並發) | API 呼叫、> 500 並發 | Gevent ⚠️ 或遷移 ASGI | workers × 1000 |
| 混合型 | CRUD + 計算 + API | Gthread | workers × 4 |
| 長連接 | WebSocket、SSE | ASGI (Daphne) | 原生支持 |
| 簡單 CRUD | 內容管理、後台 | Sync | workers = CPU × 2 + 1 |
按並發需求選擇
| 並發需求 | 推薦方案 | 配置示例 |
|---|---|---|
| < 50 | Sync (4 workers) | 4 個並發 |
| 50-200 | Gthread (9 workers × 2 threads) | 18 個並發 |
| 200-500 | Gthread (9 workers × 4 threads) | 36 個並發 |
| 500-2000 | Gevent (4 workers × 1000) | 4000 個並發 |
| > 2000 | Gevent (8 workers × 2000) | 16000 個並發 |
按應用類型選擇
| 應用類型 | Worker | 理由 |
|---|---|---|
| CMS / 部落格 | Sync | 簡單可靠 |
| 電商網站 | Gthread | 混合任務 |
| API 網關 | Gevent | 高並發 I/O |
| 數據分析 | Sync | CPU 密集 |
| 即時通訊 | Gevent | 長連接 |
| 後台管理 | Sync | 低並發 |
| 微服務 | Gthread | 服務間通訊 |
| 爬蟲服務 | Gevent | 大量網絡請求 |
🎤 面試高頻問題
Q1: 如何快速判斷用哪種 Worker?
完整答案:
我會問自己三個問題:
1. 應用主要在做什麼?
- 計算 → Sync
- 等待(API、資料庫)→ Gevent 或 Gthread
- 混合 → Gthread
2. 並發需求有多高?
- < 50 → Sync
- 50-500 → Gthread
500 → Gevent
3. 每個請求多久?
- < 100ms → Sync
- 100ms-1s → Gthread
1s → Gevent
舉例: 如果是電商網站,有資料庫查詢、業務計算、可能呼叫支付 API,並發約 200 個,我會選擇 Gthread Worker,配置 9 workers × 4 threads = 36 並發。
Q2: 什麼情況下不能用 Gevent?
完整答案:
以下情況不適合使用 Gevent:
1. CPU 密集型任務
- 協程無法利用多核
- 一個計算密集的請求會阻塞其他協程
- 例如:數據分析、機器學習推理
2. 不兼容 Monkey Patch 的庫
- 某些 C 擴展庫無法被 Patch
- 例如:某些資料庫驅動、加密庫
- 需要檢查兼容性
3. 團隊不熟悉協程
- 除錯困難
- 學習成本高
- 維護成本大
4. 需要真正的多線程
- 某些場景需要原生線程
- Gevent 的「協程」不是真正的線程
這些情況應該選擇 Sync 或 Gthread Worker。
Q3: 給你一個新專案,你會如何選擇 Worker?
完整答案:
我會按照以下步驟評估:
Step 1:分析應用特性(5 分鐘)
# 統計代碼中的操作比例 CPU 操作(計算、處理):20% I/O 操作(資料庫、API):70% 其他:10%Step 2:預估並發需求(10 分鐘)
- 預期用戶數:1000 人
- 同時在線:100 人
- 並發請求:50 個
Step 3:選擇 Worker(1 分鐘)
- I/O 佔 70% → 排除 Sync
- 並發 50 個 → Gthread 或 Gevent
- 混合型任務 → 選擇 Gthread
Step 4:初始配置
gunicorn myproject.wsgi:application \ --workers 9 \ --worker-class gthread \ --threads 4 # 總並發:36 個Step 5:壓力測試調整(1 小時)
- 使用 locust 或 ab 測試
- 調整 workers 和 threads
- 找到最佳配置
Step 6:監控生產環境(持續)
- 觀察 CPU、記憶體
- 調整配置
- 根據實際負載優化
Q4: Sync、Gevent、Gthread 各自最大的問題是什麼?
完整答案:
Sync Worker:
- 最大問題:I/O 等待浪費
- 場景:呼叫外部 API 需要 2 秒,Worker 就閒置 2 秒
- 影響:並發能力低,資源利用率差
- 解決:用 Gevent 或 Gthread
Gevent Worker:
- 最大問題:CPU 密集型任務無效
- 場景:複雜計算時無法切換協程,阻塞其他請求
- 影響:一個慢請求影響所有請求
- 解決:CPU 密集任務用 Sync 或移到 Celery
Gthread Worker:
- 最大問題:GIL 限制真正並行
- 場景:多線程 CPU 計算無法真正並行
- 影響:CPU 密集型效能不如 Sync
- 解決:純 CPU 密集用 Sync
總結:
- 沒有完美的 Worker
- 要根據應用特性選擇
- 必要時可以混用(多個 Gunicorn 實例)
✅ 重點回顧
選擇原則
按任務類型:
- CPU 密集 → Sync
- I/O 密集(低並發)→ Gthread
- I/O 密集(高並發)→ Gevent
- 混合型 → Gthread
按並發需求:
- < 50 → Sync
- 50-500 → Gthread
500 → Gevent
按應用場景:
- CMS、後台 → Sync
- 電商、企業應用 → Gthread
- API 網關、聊天 → Gevent
- 數據分析 → Sync
配置建議
Sync Worker:
workers = CPU_cores # CPU 密集
workers = (2 × CPU_cores) + 1 # 混合型Gevent Worker:
workers = (2 × CPU_cores) + 1
worker-connections = 1000-2000
# 記得 Monkey Patch!Gthread Worker:
workers = (2 × CPU_cores) + 1
threads = 2-4 # 低並發
threads = 4-8 # 高並發決策檢查清單
- 分析了應用的 CPU vs I/O 比例
- 評估了並發需求
- 考慮了團隊技術棧
- 檢查了第三方庫兼容性
- 規劃了測試和監控方案
- 準備了 Plan B(備用方案)
🎉 基礎篇完成!
恭喜你完成 Gunicorn Workers 基礎篇!
你現在已經掌握:
- ✅ Gunicorn 的作用和重要性
- ✅ Worker 的本質和工作原理
- ✅ Sync、Gevent、Gthread 的特性
- ✅ 如何根據場景選擇 Worker
- ✅ 面試高頻問題的標準答案
📚 接下來的學習路徑
如果你在做新專案(Django 3.0+):
👉 強烈推薦:01-8. 現代方案:Gunicorn + Uvicorn Workers 🆕 了解如何使用現代 ASGI 方案部署 Django 專案
如果你要面試:
👉 直接跳到:05-1. 面試常見問題(基礎)
如果你在維護舊專案(WSGI):
👉 繼續學習:02-1. Workers 數量計算
如果你要深入:
👉 學習進階:03-1. 案例:I/O 密集型應用
🤓 最終測驗
測試你是否真正掌握了!
情境 1
一個社交媒體 API,需要呼叫 3 個外部服務獲取數據,預計並發 300 個,你會選什麼?
答案:Gthread Worker
理由:
- I/O 密集(外部服務呼叫)
- 並發 300(Gthread 範圍:50-500)
- 配置:9 workers × 4 threads = 36 並發(可能需要調整到 10 workers × 4 threads)
情境 2
一個數據分析平台,用戶上傳 CSV 進行複雜統計,同時用戶不多(< 30),你會選什麼?
答案:Sync Worker
理由:
- CPU 密集(數據分析、統計計算)
- 低並發(< 30)
- 配置:workers = CPU_cores(例如 4 核就 4 workers)
情境 3
一個爬蟲服務,需要同時抓取 1000 個網頁,你會選什麼?
答案:Gevent Worker
理由:
- I/O 密集(網絡請求)
- 超高並發(1000 個)
- 配置:4 workers × 2000 connections = 8000 並發
- 必須加 Monkey Patch
情境 4
一個電商網站,有購物車、結帳、推薦系統,並發約 150,你會選什麼?
答案:Gthread Worker
理由:
- 混合型(資料庫 + 計算 + 可能的外部服務)
- 中等並發(150)
- 配置:9 workers × 4 threads = 36 並發(可能需要調整到 12 workers × 4 threads)
上一篇: 01-6. Gthread Worker 詳解 下一篇: 01-8. 現代方案:Gunicorn + Uvicorn Workers 🆕 或 05-1. 面試常見問題(基礎)
最後更新:2025-10-30