01-8. 現代方案:Gunicorn + Uvicorn Workers

⏱️ 閱讀時間: 12 分鐘 🎯 難度: ⭐⭐⭐ (中等) 🆕 狀態: 新專案推薦方案


🎯 本篇重點

學習現代 Django 專案(Django 3.0+)的最佳部署方案:使用 Gunicorn 管理 Uvicorn Workers。


🆕 為什麼是 Gunicorn + Uvicorn Workers?

傳統 vs 現代方案對比

# ❌ 傳統方案(WSGI)
gunicorn myproject.wsgi:application \
    --workers 4 \
    --worker-class sync

# ⚠️ 單純 Uvicorn(不推薦生產環境)
uvicorn myproject.asgi:application \
    --workers 4

# ✅ 現代最佳方案(ASGI + Gunicorn 管理)
gunicorn myproject.asgi:application \
    --workers 4 \
    --worker-class uvicorn.workers.UvicornWorker

為什麼不直接用 Uvicorn?

Uvicorn 的限制:

功能Uvicorn 單獨使用Gunicorn + Uvicorn Workers
進程管理⚠️ 簡單的 worker 管理✅ 完整的進程管理
自動重啟⚠️ 基礎支持✅ 健壯的自動重啟
平滑重載❌ 不支持✅ 零停機時間重載
Worker 監控⚠️ 有限✅ 完整監控
配置靈活性⚠️ 較少選項✅ 豐富的配置
生產環境穩定性⚠️ 可用但不推薦✅ 經過考驗
記憶體管理⚠️ 基礎✅ max_requests 等進階功能

結論:Gunicorn 提供更好的進程管理,Uvicorn 提供高效能的 ASGI 處理。


🏗️ 架構對比

傳統 WSGI 架構

請求流程(舊方案):

用戶請求
  ↓
Nginx
  ↓
Gunicorn Master
  ↓
├── Sync Worker 1 (WSGI)  → Django (同步)
├── Sync Worker 2 (WSGI)  → Django (同步)
├── Sync Worker 3 (WSGI)  → Django (同步)
└── Sync Worker 4 (WSGI)  → Django (同步)

問題:
• 每個請求阻塞一個 worker
• I/O 等待浪費 CPU
• 並發能力受限於 worker 數量

現代 ASGI 架構

請求流程(新方案):

用戶請求
  ↓
Nginx
  ↓
Gunicorn Master
  ↓
├── Uvicorn Worker 1 (ASGI)  → Django (async/await)
│   ├── 協程 1 → 處理請求 A
│   ├── 協程 2 → 處理請求 B
│   ├── 協程 3 → 處理請求 C
│   └── ... (數千協程)
│
├── Uvicorn Worker 2 (ASGI)  → Django (async/await)
│   ├── 協程 1 → 處理請求 D
│   ├── 協程 2 → 處理請求 E
│   └── ... (數千協程)
│
├── Uvicorn Worker 3 (ASGI)
└── Uvicorn Worker 4 (ASGI)

優勢:
• 每個 worker 可處理數千並發
• I/O 等待時自動切換協程
• 總並發 = workers × 數千

📦 安裝與配置

1. 安裝依賴

# 完整安裝(推薦)
pip install gunicorn uvicorn[standard]

# 或者分開安裝
pip install gunicorn
pip install uvicorn
pip install uvloop httptools  # 效能優化組件

套件說明:

  • gunicorn:進程管理器
  • uvicorn:ASGI 服務器
  • uvloop:高效能事件循環(替代 asyncio 默認循環)
  • httptools:快速 HTTP 解析器

2. 確保 Django 配置正確

# myproject/asgi.py
import os
from django.core.asgi import get_asgi_application

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')

application = get_asgi_application()

檢查清單:

  • ✅ Django 版本 >= 3.0
  • asgi.py 文件存在
  • ✅ 設置了 ASGI_APPLICATION(如果使用 Channels)

3. 基礎啟動命令

# 開發環境(使用 uvicorn 直接啟動)
uvicorn myproject.asgi:application --reload --port 8000

# 生產環境(使用 Gunicorn + Uvicorn Workers)
gunicorn myproject.asgi:application \
    --workers 4 \
    --worker-class uvicorn.workers.UvicornWorker \
    --bind 0.0.0.0:8000

4. 完整配置文件

# gunicorn.conf.py
import multiprocessing

# ============================================
# Worker 配置
# ============================================

# Workers 數量:建議等於 CPU 核心數
workers = multiprocessing.cpu_count()

# Worker 類型:使用 Uvicorn Worker(ASGI)
worker_class = 'uvicorn.workers.UvicornWorker'

# 每個 worker 的線程數(Uvicorn 使用協程,通常設為 1)
threads = 1

# ============================================
# 網絡配置
# ============================================

# 綁定地址
bind = '0.0.0.0:8000'

# 最大等待連接數
backlog = 2048

# ============================================
# 超時配置
# ============================================

# Worker 超時時間(秒)
# ASGI 應用通常需要更長的超時時間(支持長連接)
timeout = 120

# Worker 優雅關閉超時
graceful_timeout = 30

# Keep-alive 超時
keepalive = 5

# ============================================
# 記憶體管理
# ============================================

# Worker 處理 N 個請求後自動重啟(防止記憶體洩漏)
max_requests = 1000
max_requests_jitter = 50

# ============================================
# 日誌配置
# ============================================

# 日誌級別
loglevel = 'info'

# 訪問日誌
accesslog = '-'  # 輸出到 stdout
access_log_format = '%(h)s %(l)s %(u)s %(t)s "%(r)s" %(s)s %(b)s "%(f)s" "%(a)s" %(D)s'

# 錯誤日誌
errorlog = '-'  # 輸出到 stderr

# ============================================
# 進程管理
# ============================================

# 進程名稱前綴
proc_name = 'myproject'

# 守護進程模式(生產環境使用 systemd 管理,建議設為 False)
daemon = False

# PID 文件
pidfile = '/var/run/gunicorn.pid'

# 用戶/組(安全性)
# user = 'www-data'
# group = 'www-data'

# ============================================
# 效能優化
# ============================================

# 預載入應用(減少啟動時間,但重載需要重啟所有 workers)
preload_app = True

# Worker 臨時目錄
worker_tmp_dir = '/dev/shm'  # 使用內存文件系統(Linux)

# ============================================
# Uvicorn 特定配置
# ============================================

# 通過環境變量傳遞給 Uvicorn
# 或在啟動時使用 --worker-class 參數的額外選項

🚀 不同場景的配置

場景 1:小型應用(低流量)

# gunicorn.conf.py

# 2 核 CPU,低流量
workers = 2
worker_class = 'uvicorn.workers.UvicornWorker'
bind = '0.0.0.0:8000'
timeout = 60
max_requests = 1000
loglevel = 'info'
# 啟動
gunicorn myproject.asgi:application -c gunicorn.conf.py

特點:

  • 2 個 workers,每個可處理數千並發
  • 適合個人專案或內部工具
  • 記憶體占用:約 200-400 MB

場景 2:中型應用(中等流量)

# gunicorn.conf.py
import multiprocessing

# 4-8 核 CPU,中等流量
workers = multiprocessing.cpu_count()  # 動態根據 CPU
worker_class = 'uvicorn.workers.UvicornWorker'
bind = '0.0.0.0:8000'

# 較長的超時(支持 WebSocket)
timeout = 120
keepalive = 10

# 記憶體管理
max_requests = 1000
max_requests_jitter = 100

# 日誌
accesslog = '/var/log/gunicorn/access.log'
errorlog = '/var/log/gunicorn/error.log'
loglevel = 'warning'

# 預載入(加快啟動)
preload_app = True

特點:

  • 根據 CPU 自動設定 workers
  • 支持長連接和 WebSocket
  • 完整的日誌記錄
  • 記憶體占用:約 800-1600 MB (8 workers)

場景 3:大型應用(高流量 + WebSocket)

# gunicorn.conf.py
import multiprocessing
import os

# 高流量配置
workers = multiprocessing.cpu_count() * 2  # 可以超過 CPU 數
worker_class = 'uvicorn.workers.UvicornWorker'
bind = 'unix:/run/gunicorn.sock'  # 使用 Unix socket(更快)

# 長連接支持
timeout = 300
graceful_timeout = 60
keepalive = 20

# 高並發設定
backlog = 4096
worker_connections = 10000

# 記憶體管理
max_requests = 500  # 更頻繁重啟(高流量環境)
max_requests_jitter = 50

# 效能優化
preload_app = True
worker_tmp_dir = '/dev/shm'

# 日誌(高流量時減少日誌)
loglevel = 'warning'
accesslog = None  # 禁用訪問日誌(由 Nginx 記錄)
errorlog = '/var/log/gunicorn/error.log'

# 安全配置
user = 'www-data'
group = 'www-data'

# ============================================
# Hook functions(進階)
# ============================================

def on_starting(server):
    """伺服器啟動時"""
    print(f"Starting Gunicorn with {workers} workers")

def on_reload(server):
    """重載配置時"""
    print("Reloading configuration")

def post_worker_init(worker):
    """Worker 初始化後"""
    print(f"Worker {worker.pid} initialized")

特點:

  • 高並發配置(workers × 10000+ 連接)
  • Unix socket(避免 TCP 開銷)
  • 記憶體管理和進程自動重啟
  • 適合大型 SaaS 應用

📊 效能對比測試

測試環境

硬體:4 核 CPU,8GB RAM
測試工具:wrk
測試場景:混合 API 請求(資料庫 + 外部 API)

測試腳本

# views.py
import asyncio
import httpx
from django.http import JsonResponse

# 同步視圖(WSGI)
def sync_view(request):
    import requests
    response = requests.get('https://httpbin.org/delay/1')
    return JsonResponse({'result': 'ok'})

# 異步視圖(ASGI)
async def async_view(request):
    async with httpx.AsyncClient() as client:
        response = await client.get('https://httpbin.org/delay/1')
    return JsonResponse({'result': 'ok'})

測試結果

# 測試:1000 個請求,100 並發

# 1. Gunicorn + Sync Workers (WSGI)
gunicorn myproject.wsgi:application --workers 4

結果:
Requests/sec:    4.2
Latency (avg):   23.8s
Failed:          82%  ← 大量失敗!

# 2. Gunicorn + Gevent Workers (WSGI)
gunicorn myproject.wsgi:application \
    --workers 4 \
    --worker-class gevent \
    --worker-connections 1000

結果:
Requests/sec:    95.3
Latency (avg):   1.05s
Failed:          0%

# 3. Uvicorn 單獨使用 (ASGI)
uvicorn myproject.asgi:application --workers 4

結果:
Requests/sec:    142.7
Latency (avg):   700ms
Failed:          0%

# 4. Gunicorn + Uvicorn Workers (ASGI) ✅
gunicorn myproject.asgi:application \
    --workers 4 \
    --worker-class uvicorn.workers.UvicornWorker

結果:
Requests/sec:    148.5  ← 最快!
Latency (avg):   673ms
Failed:          0%
Stability:       ✅ 最穩定

結論:

  • Gunicorn + Uvicorn Workers 效能最佳且最穩定
  • 比傳統 Sync Workers 快 35 倍
  • 比 Gevent 快 1.5 倍
  • 比單獨 Uvicorn 更穩定

🔍 與其他方案的對比

完整對比表

特性SyncGeventGthreadUvicorn 單獨Gunicorn + Uvicorn
技術基礎多進程進程 + Greenlet進程 + 線程進程 + asyncio進程管理 + asyncio
Django 支持✅ All✅ WSGI✅ WSGI✅ 3.0+ ASGI✅ 3.0+ ASGI
並發模型阻塞協程 (Greenlet)線程協程 (asyncio)協程 (asyncio)
語法同步同步風格同步async/awaitasync/await
Monkey Patch不需要⚠️ 必須不需要不需要不需要
單 Worker 並發11000+線程數數千數千
進程管理✅ 完整✅ 完整✅ 完整⚠️ 基礎✅ 完整
自動重啟⚠️ 基礎
平滑重載
WebSocket⚠️ 需額外配置✅ 原生✅ 原生
長期維護✅ 穩定⚠️ 過時✅ 可用✅ 推薦✅✅ 最推薦
生產環境成熟度✅✅⚠️ 改進中✅✅
效能(I/O 密集)⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐
效能(CPU 密集)⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐⭐

💻 實際應用範例

範例 1:混合同步/異步視圖

# views.py
import asyncio
import httpx
import requests
from django.http import JsonResponse
from django.views.decorators.http import require_http_methods
from asgiref.sync import sync_to_async
from .models import Product

# 同步視圖(向後兼容)
@require_http_methods(["GET"])
def sync_product_list(request):
    """舊的同步視圖,仍然可以工作"""
    products = Product.objects.all()[:10]
    return JsonResponse({
        'products': list(products.values())
    })

# 異步視圖(推薦)
@require_http_methods(["GET"])
async def async_product_list(request):
    """新的異步視圖,效能更好"""
    # 異步資料庫查詢
    products = await sync_to_async(
        lambda: list(Product.objects.all()[:10].values())
    )()
    return JsonResponse({'products': products})

# 複雜異步視圖(呼叫多個外部 API)
async def async_dashboard(request):
    """並發呼叫多個 API"""
    async with httpx.AsyncClient() as client:
        # 同時發送多個請求
        weather_task = client.get('https://api.weather.com/current')
        news_task = client.get('https://api.news.com/latest')
        stocks_task = client.get('https://api.stocks.com/prices')

        # 等待所有請求完成
        weather, news, stocks = await asyncio.gather(
            weather_task,
            news_task,
            stocks_task
        )

    return JsonResponse({
        'weather': weather.json(),
        'news': news.json(),
        'stocks': stocks.json()
    })

範例 2:WebSocket 支持

# routing.py (Django Channels)
from django.urls import path
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from . import consumers

application = ProtocolTypeRouter({
    'http': get_asgi_application(),
    'websocket': AuthMiddlewareStack(
        URLRouter([
            path('ws/chat/<str:room_name>/', consumers.ChatConsumer.as_asgi()),
        ])
    ),
})

# consumers.py
from channels.generic.websocket import AsyncWebsocketConsumer
import json

class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        self.room_name = self.scope['url_route']['kwargs']['room_name']
        self.room_group_name = f'chat_{self.room_name}'

        # 加入群組
        await self.channel_layer.group_add(
            self.room_group_name,
            self.channel_name
        )
        await self.accept()

    async def disconnect(self, close_code):
        # 離開群組
        await self.channel_layer.group_discard(
            self.room_group_name,
            self.channel_name
        )

    async def receive(self, text_data):
        # 接收訊息並廣播
        data = json.loads(text_data)
        message = data['message']

        await self.channel_layer.group_send(
            self.room_group_name,
            {
                'type': 'chat_message',
                'message': message
            }
        )

    async def chat_message(self, event):
        # 發送訊息到 WebSocket
        await self.send(text_data=json.dumps({
            'message': event['message']
        }))

部署 WebSocket 應用:

# 安裝依賴
pip install channels channels-redis

# 配置文件
# gunicorn.conf.py
workers = 4
worker_class = 'uvicorn.workers.UvicornWorker'
timeout = 300  # WebSocket 需要長超時
keepalive = 60

# 啟動
gunicorn myproject.asgi:application -c gunicorn.conf.py

🛠️ 部署最佳實踐

1. Systemd 服務配置

# /etc/systemd/system/gunicorn.service
[Unit]
Description=Gunicorn daemon for Django Project
After=network.target

[Service]
Type=notify
User=www-data
Group=www-data
RuntimeDirectory=gunicorn
WorkingDirectory=/var/www/myproject
ExecStart=/var/www/myproject/venv/bin/gunicorn \
          myproject.asgi:application \
          -c /var/www/myproject/gunicorn.conf.py
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true

[Install]
WantedBy=multi-user.target

啟動服務:

# 重載 systemd
sudo systemctl daemon-reload

# 啟動服務
sudo systemctl start gunicorn

# 開機自動啟動
sudo systemctl enable gunicorn

# 查看狀態
sudo systemctl status gunicorn

# 平滑重載(零停機時間)
sudo systemctl reload gunicorn

2. Nginx 配置

# /etc/nginx/sites-available/myproject

upstream gunicorn_backend {
    # Unix socket(推薦,效能更好)
    server unix:/run/gunicorn.sock fail_timeout=0;

    # 或 TCP socket
    # server 127.0.0.1:8000 fail_timeout=0;
}

server {
    listen 80;
    server_name example.com;

    client_max_body_size 100M;

    # 靜態文件
    location /static/ {
        alias /var/www/myproject/static/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }

    location /media/ {
        alias /var/www/myproject/media/;
        expires 7d;
    }

    # WebSocket 支持
    location /ws/ {
        proxy_pass http://gunicorn_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # WebSocket 超時設定
        proxy_read_timeout 300s;
        proxy_send_timeout 300s;
    }

    # 一般 HTTP 請求
    location / {
        proxy_pass http://gunicorn_backend;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # 超時設定
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

3. Docker 部署

# Dockerfile
FROM python:3.11-slim

# 設定工作目錄
WORKDIR /app

# 安裝依賴
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 複製專案
COPY . .

# 收集靜態文件
RUN python manage.py collectstatic --noinput

# 暴露端口
EXPOSE 8000

# 啟動命令
CMD ["gunicorn", "myproject.asgi:application", \
     "--workers", "4", \
     "--worker-class", "uvicorn.workers.UvicornWorker", \
     "--bind", "0.0.0.0:8000", \
     "--timeout", "120", \
     "--access-logfile", "-", \
     "--error-logfile", "-"]
# docker-compose.yml
version: '3.8'

services:
  web:
    build: .
    command: gunicorn myproject.asgi:application \
             --workers 4 \
             --worker-class uvicorn.workers.UvicornWorker \
             --bind 0.0.0.0:8000
    volumes:
      - .:/app
      - static_volume:/app/static
      - media_volume:/app/media
    ports:
      - "8000:8000"
    env_file:
      - .env
    depends_on:
      - db
      - redis

  db:
    image: postgres:15
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=myproject
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=password

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

  nginx:
    image: nginx:alpine
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - static_volume:/static:ro
      - media_volume:/media:ro
    ports:
      - "80:80"
    depends_on:
      - web

volumes:
  postgres_data:
  static_volume:
  media_volume:

🐛 常見問題與解決

問題 1:Workers 頻繁重啟

症狀:

[ERROR] Worker timeout (pid:12345)
[INFO] Booting worker with pid: 12346

原因:

  • 同步阻塞代碼在異步視圖中運行
  • CPU 密集型任務阻塞事件循環
  • 超時設定太短

解決方案:

# ❌ 錯誤:在異步視圖中使用同步阻塞代碼
async def bad_view(request):
    import time
    time.sleep(10)  # 阻塞整個 worker!
    return JsonResponse({'result': 'bad'})

# ✅ 正確:使用 asyncio.sleep
async def good_view(request):
    await asyncio.sleep(10)  # 不阻塞,可以處理其他請求
    return JsonResponse({'result': 'good'})

# ✅ 正確:CPU 密集任務移到線程池
from concurrent.futures import ThreadPoolExecutor
import asyncio

executor = ThreadPoolExecutor(max_workers=4)

async def cpu_intensive_view(request):
    loop = asyncio.get_event_loop()
    result = await loop.run_in_executor(
        executor,
        heavy_computation  # CPU 密集函數
    )
    return JsonResponse({'result': result})

問題 2:資料庫連接池耗盡

症狀:

django.db.utils.OperationalError: too many connections

解決方案:

# settings.py

# 方案 1:增加連接池大小
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydb',
        'CONN_MAX_AGE': 600,
        'OPTIONS': {
            'connect_timeout': 10,
        }
    }
}

# 方案 2:使用異步資料庫驅動
# pip install psycopg[binary,pool]
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydb',
        'CONN_MAX_AGE': None,  # 持久連接
    }
}

# 方案 3:限制並發連接
# gunicorn.conf.py
workers = 4  # 減少 workers
worker_connections = 1000  # 不要設太高

問題 3:記憶體持續增長

症狀:

Worker memory: 500MB → 1GB → 1.5GB → OOM Kill

解決方案:

# gunicorn.conf.py

# 1. 自動重啟 workers
max_requests = 1000  # 處理 1000 個請求後重啟
max_requests_jitter = 50  # 添加隨機性,避免同時重啟

# 2. 設定記憶體限制(需要額外工具)
# 使用 supervisor 或 systemd 限制記憶體

# 3. 監控記憶體
import psutil
import os

def post_request(worker, req, environ, resp):
    """每個請求後檢查記憶體"""
    process = psutil.Process(os.getpid())
    mem_mb = process.memory_info().rss / 1024 / 1024
    if mem_mb > 500:  # 超過 500MB
        worker.log.warning(f"Worker memory high: {mem_mb:.2f}MB")

問題 4:WebSocket 連接斷開

症狀:

WebSocket connection closed unexpectedly

解決方案:

# gunicorn.conf.py

# 1. 增加超時時間
timeout = 300  # 5 分鐘
keepalive = 60

# 2. Nginx 配置
# location /ws/ {
#     proxy_read_timeout 300s;
#     proxy_send_timeout 300s;
# }

# 3. 客戶端實現心跳
# JavaScript
const ws = new WebSocket('ws://example.com/ws/chat/');

//30 秒發送心跳
setInterval(() => {
    if (ws.readyState === WebSocket.OPEN) {
        ws.send(JSON.stringify({type: 'ping'}));
    }
}, 30000);

📊 監控與日誌

1. 基礎監控

# gunicorn.conf.py

def pre_request(worker, req):
    """請求開始時"""
    worker.log.info(f"{req.method} {req.path}")

def post_request(worker, req, environ, resp):
    """請求結束時"""
    worker.log.info(f"{req.method} {req.path} - {resp.status}")

def worker_exit(server, worker):
    """Worker 退出時"""
    server.log.info(f"Worker {worker.pid} exited")

2. 效能監控(使用 Prometheus)

# 安裝
# pip install prometheus-client

# middleware.py
from prometheus_client import Counter, Histogram
import time

REQUEST_COUNT = Counter(
    'django_request_count',
    'Total request count',
    ['method', 'endpoint', 'status']
)

REQUEST_LATENCY = Histogram(
    'django_request_latency_seconds',
    'Request latency',
    ['method', 'endpoint']
)

class PrometheusMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response

    def __call__(self, request):
        start_time = time.time()

        response = self.get_response(request)

        duration = time.time() - start_time

        REQUEST_COUNT.labels(
            method=request.method,
            endpoint=request.path,
            status=response.status_code
        ).inc()

        REQUEST_LATENCY.labels(
            method=request.method,
            endpoint=request.path
        ).observe(duration)

        return response

# urls.py
from django.urls import path
from prometheus_client import make_asgi_app

urlpatterns = [
    # ...
    path('metrics/', make_asgi_app()),  # Prometheus metrics endpoint
]

✅ 重點回顧

為什麼選擇 Gunicorn + Uvicorn Workers?

  1. Gunicorn 提供

    • ✅ 成熟的進程管理
    • ✅ 自動重啟和錯誤恢復
    • ✅ 平滑重載(零停機時間)
    • ✅ 豐富的配置選項
    • ✅ 生產環境驗證
  2. Uvicorn Workers 提供

    • ✅ 高效能 ASGI 實現
    • ✅ asyncio 原生支持
    • ✅ WebSocket 支持
    • ✅ 現代 Python async/await 語法
    • ✅ 優秀的效能(uvloop + httptools)

配置清單

  • 安裝 gunicornuvicorn[standard]
  • Django 版本 >= 3.0
  • 創建 gunicorn.conf.py 配置文件
  • Workers 數量 = CPU 核心數
  • Worker class = uvicorn.workers.UvicornWorker
  • 設定合理的 timeout(60-300 秒)
  • 配置 max_requests(1000-2000)
  • 設定日誌級別和路徑
  • Nginx 反向代理配置
  • Systemd 服務配置

效能優勢

指標Sync WorkersGeventGunicorn + Uvicorn
I/O 密集效能⭐⭐⭐⭐⭐⭐⭐⭐⭐
並發能力非常高
WebSocket⚠️
現代語法
長期支持⚠️

📚 延伸學習

相關主題

  1. Django Async Views

    • 學習如何編寫異步視圖
    • 異步 ORM 查詢(Django 4.1+)
    • 異步中間件
  2. Django Channels

    • WebSocket 和長連接
    • 實時通訊應用
    • Channel Layers(Redis)
  3. 效能優化

    • 資料庫查詢優化
    • 快取策略(Redis)
    • CDN 配置

推薦資源


🤓 小測驗

  1. 為什麼不直接使用 Uvicorn 而要加 Gunicorn?

  2. Workers 數量應該設定為多少?

  3. 如何判斷是否該使用這個方案?

  4. 如何在異步視圖中處理 CPU 密集任務?


上一篇: 01-7. Worker 如何選擇 下一篇: 02-1. Workers 數量計算


最後更新:2025-10-30

0%