Django 面試精華:ASGI vs WSGI
從同步到異步:理解 Python Web 服務器接口的演進
前言
當你部署 Django 應用時,可能會看到兩種不同的啟動方式:
# 傳統方式:WSGI
gunicorn myproject.wsgi:application
# 新方式:ASGI
uvicorn myproject.asgi:application這兩者有什麼區別?什麼時候該用哪個?
想像一個實際場景:
# 傳統同步視圖(WSGI)
def get_user_data(request):
user = User.objects.get(id=request.user.id) # 數據庫查詢:100ms
posts = Post.objects.filter(user=user) # 數據庫查詢:150ms
comments = Comment.objects.filter(user=user) # 數據庫查詢:120ms
# 總耗時:370ms(串行執行)
return JsonResponse({
'user': user,
'posts': posts,
'comments': comments,
})
# 異步視圖(ASGI)
async def get_user_data(request):
# 並行執行三個查詢!
user, posts, comments = await asyncio.gather(
User.objects.aget(id=request.user.id), # 100ms
Post.objects.filter(user=user).all(), # 150ms
Comment.objects.filter(user=user).all(), # 120ms
)
# 總耗時:150ms(並行執行,取最慢的那個)
return JsonResponse({
'user': user,
'posts': posts,
'comments': comments,
})異步能將響應時間從 370ms 降到 150ms! 但這需要 ASGI 的支持。
這篇文章將深入比較 WSGI 和 ASGI,幫助你做出正確的技術選型。
1. WSGI:同步時代的標準
1.1 什麼是 WSGI
WSGI (Web Server Gateway Interface) 是 Python Web 服務器和 Web 應用之間的標準接口,定義於 PEP 3333。
定義(2003 年發布):
- 一個簡單的同步調用約定
- 服務器調用應用,應用返回響應
- 一次請求,一個線程/進程
1.2 WSGI 工作原理
請求處理流程:
客戶端請求
↓
Nginx (反向代理)
↓
Gunicorn (WSGI 服務器)
↓ 分配 Worker
Worker 1 (線程/進程) → Django 應用
Worker 2 (線程/進程) → Django 應用
Worker 3 (線程/進程) → Django 應用
↓
返回響應WSGI 應用示例:
# wsgi.py
def application(environ, start_response):
"""
WSGI 應用的標準簽名
Args:
environ: 包含請求資訊的字典(HTTP headers, method, path 等)
start_response: 用於返回狀態碼和響應頭的回調函數
Returns:
可迭代對象(通常是列表),包含響應體
"""
# 解析請求
path = environ['PATH_INFO']
method = environ['REQUEST_METHOD']
# 設置響應
status = '200 OK'
headers = [('Content-Type', 'text/plain')]
start_response(status, headers)
# 返回響應體
return [b'Hello World!']
# Django 的 WSGI 應用
from django.core.wsgi import get_wsgi_application
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
application = get_wsgi_application()1.3 WSGI 的特點
| 特性 | 說明 |
|---|---|
| 同步模型 | 一個請求占用一個線程/進程,直到完成 |
| 阻塞 I/O | 數據庫查詢、文件讀寫會阻塞線程 |
| 成熟穩定 | 20+ 年歷史,生態完善 |
| 簡單易懂 | 概念簡單,調試方便 |
| 並發限制 | 受限於 Worker 數量(通常 2-4 × CPU 核心數) |
阻塞示例:
def sync_view(request):
# 1. 查詢數據庫(阻塞 100ms)
user = User.objects.get(id=1) # 線程等待...
# 2. 調用外部 API(阻塞 500ms)
response = requests.get('https://api.example.com/data') # 線程等待...
# 3. 讀取文件(阻塞 50ms)
with open('data.txt') as f: # 線程等待...
data = f.read()
# 總耗時:650ms
# 在這 650ms 內,Worker 線程被占用,無法處理其他請求
return HttpResponse('OK')並發處理能力:
假設:
- Gunicorn 配置:4 個 Worker
- 每個請求平均耗時:1 秒
理論 QPS:4 請求/秒
━━━━━━━━━━━━━━━━━━━━━━━━
Worker 1: [請求1-1秒][請求5-1秒][請求9-1秒]
Worker 2: [請求2-1秒][請求6-1秒][請求10-1秒]
Worker 3: [請求3-1秒][請求7-1秒][請求11-1秒]
Worker 4: [請求4-1秒][請求8-1秒][請求12-1秒]
第 5 個請求必須等待 Worker 空閒2. ASGI:異步時代的標準
2.1 什麼是 ASGI
ASGI (Asynchronous Server Gateway Interface) 是 WSGI 的精神繼承者,為異步 Python Web 應用設計。
定義(2016 年發布,Django Channels 項目啟動):
- 支持異步和同步應用
- 支持長連接(WebSocket、Server-Sent Events)
- 支持 HTTP/2
- 一個 Worker 可以同時處理多個請求
2.2 ASGI 工作原理
請求處理流程:
客戶端請求
↓
Nginx (反向代理)
↓
Uvicorn/Daphne (ASGI 服務器)
↓ 事件循環
單個 Worker (事件循環)
├─ 協程 1 → Django 應用
├─ 協程 2 → Django 應用
├─ 協程 3 → Django 應用
└─ 協程 N → Django 應用
↓
返回響應ASGI 應用示例:
# asgi.py
async def application(scope, receive, send):
"""
ASGI 應用的標準簽名
Args:
scope: 包含連接資訊的字典(類似 WSGI 的 environ)
receive: 異步可調用對象,用於接收事件(請求體)
send: 異步可調用對象,用於發送事件(響應)
"""
# 檢查連接類型
if scope['type'] == 'http':
# HTTP 請求
await send({
'type': 'http.response.start',
'status': 200,
'headers': [[b'content-type', b'text/plain']],
})
await send({
'type': 'http.response.body',
'body': b'Hello World!',
})
elif scope['type'] == 'websocket':
# WebSocket 連接
await send({'type': 'websocket.accept'})
message = await receive()
await send({
'type': 'websocket.send',
'text': 'Hello WebSocket!',
})
# Django 的 ASGI 應用
from django.core.asgi import get_asgi_application
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'myproject.settings')
application = get_asgi_application()2.3 ASGI 的特點
| 特性 | 說明 |
|---|---|
| 異步模型 | 使用 async/await 語法 |
| 非阻塞 I/O | I/O 操作不阻塞事件循環 |
| 高並發 | 單個 Worker 可處理數千個並發連接 |
| 長連接支持 | WebSocket, Server-Sent Events, HTTP/2 |
| 向後兼容 | 可以運行同步 WSGI 應用 |
非阻塞示例:
async def async_view(request):
# 1. 異步查詢數據庫(不阻塞,100ms)
user = await User.objects.aget(id=1) # 切換到其他協程
# 2. 異步調用外部 API(不阻塞,500ms)
async with aiohttp.ClientSession() as session:
async with session.get('https://api.example.com/data') as response:
data = await response.json() # 切換到其他協程
# 3. 異步讀取文件(不阻塞,50ms)
async with aiofiles.open('data.txt') as f:
content = await f.read() # 切換到其他協程
# 總耗時:650ms
# 但在這 650ms 內,Worker 可以處理其他請求!
return HttpResponse('OK')並發處理能力:
假設:
- Uvicorn 配置:2 個 Worker
- 每個請求平均耗時:1 秒(但 90% 是 I/O 等待)
理論 QPS:200+ 請求/秒(取決於 I/O 等待時間)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Worker 1 (事件循環):
協程1: [請求1--等待I/O--][處理][完成]
協程2: [請求2--等待I/O--][處理][完成]
協程3: [請求3--等待I/O--][處理][完成]
協程4: [請求4--等待I/O--][處理][完成]
... (同時處理數百個請求)
Worker 2 (事件循環):
協程1: [請求101--等待I/O--][處理][完成]
協程2: [請求102--等待I/O--][處理][完成]
... (同時處理數百個請求)
請求不需要等待 Worker 空閒!3. WSGI vs ASGI 對比
3.1 核心差異
| 特性 | WSGI | ASGI |
|---|---|---|
| 發布時間 | 2003 | 2016 |
| 執行模型 | 同步(阻塞) | 異步(非阻塞) |
| 請求處理 | 一個線程/進程一個請求 | 一個事件循環多個請求 |
| 並發模型 | 多線程/多進程 | 協程(單線程) |
| 長連接 | ❌ 不支持 | ✅ 支持 WebSocket, SSE |
| HTTP/2 | ❌ 不支持 | ✅ 支持 |
| 性能 | 受限於 Worker 數量 | 高並發(I/O 密集型) |
| 複雜度 | 簡單 | 較複雜 |
| 生態 | 非常成熟 | 快速發展 |
| Django 支持 | Django 1.0+ | Django 3.0+ |
3.2 性能對比
場景 1:I/O 密集型(數據庫查詢、API 調用)
# WSGI 同步視圖
def wsgi_view(request):
user = User.objects.get(id=1) # 100ms
posts = Post.objects.filter(user=user).all() # 200ms
return JsonResponse({'user': user, 'posts': posts})
# 總耗時:300ms
# Worker 被占用:300ms
# ASGI 異步視圖
async def asgi_view(request):
user, posts = await asyncio.gather(
User.objects.aget(id=1), # 100ms
Post.objects.filter(user=user).all(), # 200ms
)
return JsonResponse({'user': user, 'posts': posts})
# 總耗時:200ms(並行執行)
# Worker 實際占用時間:< 1ms(其餘時間可處理其他請求)性能提升:
- 響應時間:減少 33%(300ms → 200ms)
- 吞吐量:提升 10-50 倍(取決於 I/O 等待時間)
場景 2:CPU 密集型(數據處理、圖像處理)
# WSGI 同步視圖
def wsgi_cpu_intensive(request):
result = complex_calculation() # 純 CPU 計算,1 秒
return JsonResponse({'result': result})
# 總耗時:1 秒
# ASGI 異步視圖(並無優勢)
async def asgi_cpu_intensive(request):
result = complex_calculation() # 仍然阻塞事件循環,1 秒
return JsonResponse({'result': result})
# 總耗時:1 秒
# ⚠️ 而且阻塞了事件循環,影響其他協程!結論:
- ✅ I/O 密集型:ASGI 有巨大優勢
- ❌ CPU 密集型:ASGI 無優勢,甚至更差(阻塞事件循環)
3.3 支持的協議
| 協議 | WSGI | ASGI |
|---|---|---|
| HTTP/1.0 | ✅ | ✅ |
| HTTP/1.1 | ✅ | ✅ |
| HTTP/2 | ❌ | ✅ |
| WebSocket | ❌ | ✅ |
| Server-Sent Events (SSE) | ❌ (需 hack) | ✅ |
WebSocket 示例(ASGI):
# 只能在 ASGI 下運行
from channels.generic.websocket import AsyncWebsocketConsumer
import json
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
await self.accept()
async def receive(self, text_data):
data = json.loads(text_data)
message = data['message']
# 發送消息給客戶端
await self.send(text_data=json.dumps({
'message': message
}))
async def disconnect(self, close_code):
pass4. Django 中的 WSGI 和 ASGI
4.1 Django 對兩者的支持
| Django 版本 | WSGI | ASGI |
|---|---|---|
| Django < 3.0 | ✅ 完整支持 | ❌ 不支持 |
| Django 3.0 - 3.1 | ✅ 完整支持 | 🟡 實驗性支持(僅 HTTP) |
| Django 3.2+ | ✅ 完整支持 | ✅ 穩定支持(HTTP + WebSocket) |
| Django 4.0+ | ✅ 完整支持 | ✅ 完整支持(包括異步 ORM) |
4.2 項目結構對比
WSGI 項目:
myproject/
├── myproject/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py # WSGI 入口
├── myapp/
│ ├── views.py # 同步視圖
│ └── models.py
└── manage.pyASGI 項目:
myproject/
├── myproject/
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ ├── wsgi.py # WSGI 入口(向後兼容)
│ └── asgi.py # ASGI 入口(新增)
├── myapp/
│ ├── views.py # 可以是同步或異步視圖
│ ├── async_views.py # 異步視圖
│ ├── models.py
│ └── consumers.py # WebSocket 處理器(可選)
└── manage.py4.3 啟動方式對比
WSGI 啟動:
# 開發環境
python manage.py runserver # Django 開發服務器(WSGI)
# 生產環境
gunicorn myproject.wsgi:application --workers 4ASGI 啟動:
# 開發環境
python manage.py runserver # Django 3.0+ 自動檢測並使用 ASGI
# 生產環境
uvicorn myproject.asgi:application --workers 2
# 或
daphne myproject.asgi:application
# 或(推薦)
gunicorn myproject.asgi:application -k uvicorn.workers.UvicornWorker --workers 25. 常見 ASGI 服務器對比
| 服務器 | 特點 | 適用場景 | GitHub Stars |
|---|---|---|---|
| Uvicorn | 基於 uvloop, 速度極快 | 生產環境首選 | ⭐ 8.2k |
| Daphne | Django Channels 官方,穩定 | 需要 WebSocket | ⭐ 2.3k |
| Hypercorn | 支持 HTTP/2, HTTP/3 | 需要最新協議 | ⭐ 1.1k |
| Gunicorn + Uvicorn Workers | 結合 Gunicorn 的進程管理 | 生產環境(推薦) | - |
性能對比(請求/秒):
簡單 Hello World 基準測試:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Uvicorn │████████████████│ 25,000 req/s
Daphne │██████████ │ 15,000 req/s
Hypercorn │███████████ │ 17,000 req/s
Gunicorn (WSGI) │████ │ 6,000 req/s推薦配置:
# 生產環境最佳實踐
gunicorn myproject.asgi:application \
--bind 0.0.0.0:8000 \
--workers 2 \
--worker-class uvicorn.workers.UvicornWorker \
--access-logfile - \
--error-logfile -6. 何時選擇 WSGI 或 ASGI
6.1 選擇 WSGI 的場景
✅ 推薦使用 WSGI:
- 傳統 Django 應用(CRUD、表單處理)
- CPU 密集型應用(數據分析、圖像處理)
- 依賴大量同步庫(老舊的第三方包)
- 團隊不熟悉異步編程
- 追求穩定性(WSGI 生態更成熟)
示例場景:
# 傳統 CMS 系統
def article_list(request):
articles = Article.objects.filter(published=True)
return render(request, 'articles.html', {'articles': articles})
# CPU 密集型任務
def generate_report(request):
data = Report.objects.all()
pdf = generate_pdf(data) # CPU 密集型,500ms
return FileResponse(pdf)6.2 選擇 ASGI 的場景
✅ 推薦使用 ASGI:
- I/O 密集型應用(大量 API 調用、數據庫查詢)
- 需要 WebSocket(實時聊天、推送通知)
- 需要 HTTP/2
- 微服務架構(大量服務間調用)
- 高並發場景(每秒數千請求)
示例場景:
# 聚合多個微服務數據
async def dashboard(request):
user, orders, notifications, analytics = await asyncio.gather(
fetch_user_service(request.user.id),
fetch_order_service(request.user.id),
fetch_notification_service(request.user.id),
fetch_analytics_service(request.user.id),
)
# 4 個服務調用並行執行,總耗時取最慢的那個
return JsonResponse({
'user': user,
'orders': orders,
'notifications': notifications,
'analytics': analytics,
})
# WebSocket 聊天
from channels.generic.websocket import AsyncWebsocketConsumer
class ChatConsumer(AsyncWebsocketConsumer):
async def connect(self):
await self.channel_layer.group_add("chat", self.channel_name)
await self.accept()
async def receive(self, text_data):
await self.channel_layer.group_send("chat", {
'type': 'chat_message',
'message': text_data
})6.3 決策流程圖
開始
↓
需要 WebSocket 或 HTTP/2?
├─ Yes → 選擇 ASGI ✅
└─ No → 繼續
↓
應用類型?
├─ I/O 密集型(API 調用、數據庫查詢多)
│ ↓
│ 團隊熟悉異步編程?
│ ├─ Yes → 選擇 ASGI ✅(性能提升明顯)
│ └─ No → 選擇 WSGI ✅(降低複雜度)
│
├─ CPU 密集型(計算、圖像處理多)
│ ↓
│ 選擇 WSGI ✅(ASGI 無優勢)
│
└─ 混合型
↓
並發需求高(> 1000 QPS)?
├─ Yes → 選擇 ASGI ✅
└─ No → 選擇 WSGI ✅(更穩定)7. 混合部署策略
實際項目中,可以同時使用 WSGI 和 ASGI:
架構示例:
Nginx (反向代理)
↓
├─ /api/* → Uvicorn (ASGI) → 異步視圖
│ └─ 處理 I/O 密集型 API
│
├─ /ws/* → Uvicorn (ASGI) → WebSocket
│ └─ 實時功能
│
└─ /* → Gunicorn (WSGI) → 同步視圖
└─ 傳統頁面渲染Nginx 配置:
upstream asgi_backend {
server 127.0.0.1:8000; # Uvicorn
}
upstream wsgi_backend {
server 127.0.0.1:8001; # Gunicorn
}
server {
listen 80;
# ASGI 路由
location /api/ {
proxy_pass http://asgi_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /ws/ {
proxy_pass http://asgi_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
# WSGI 路由
location / {
proxy_pass http://wsgi_backend;
}
}8. 面試常見問題
Q1: WSGI 和 ASGI 的主要區別是什麼?
答案:
| 維度 | WSGI | ASGI |
|---|---|---|
| 執行模型 | 同步(阻塞) | 異步(非阻塞) |
| 並發方式 | 多線程/多進程 | 協程(事件循環) |
| 協議支持 | 僅 HTTP | HTTP, WebSocket, HTTP/2 |
| 性能 | I/O 密集型受限 | I/O 密集型優秀 |
關鍵點:ASGI 是 WSGI 的超集,向後兼容,但提供了異步和長連接支持。
Q2: 什麼時候應該從 WSGI 遷移到 ASGI?
答案:
考慮遷移的信號:
- 需要 WebSocket:實時聊天、推送通知
- I/O 瓶頸:大量外部 API 調用
- 高並發需求:Worker 數量不夠用
- 響應時間長:大量 I/O 等待時間
不建議遷移的情況:
- CPU 密集型應用:ASGI 無優勢
- 依賴同步庫:遷移成本高
- 團隊經驗不足:增加複雜度
Q3: Django 可以同時支持 WSGI 和 ASGI 嗎?
答案:
可以!Django 3.0+ 同時提供 wsgi.py 和 asgi.py:
# 同一個 Django 項目
myproject/
├── wsgi.py # WSGI 入口
└── asgi.py # ASGI 入口
# 可以選擇不同的啟動方式
gunicorn myproject.wsgi:application # WSGI
uvicorn myproject.asgi:application # ASGI而且同一個視圖可以同時在兩種模式下運行:
# 同步視圖在 WSGI 和 ASGI 下都能運行
def sync_view(request):
return HttpResponse("OK")
# 異步視圖只能在 ASGI 下運行
async def async_view(request):
return HttpResponse("OK")Q4: Uvicorn 和 Daphne 該選哪個?
答案:
| 特性 | Uvicorn | Daphne |
|---|---|---|
| 性能 | 更快(基於 uvloop) | 較慢 |
| 穩定性 | 穩定 | 非常穩定 |
| 社區 | 活躍 | Django Channels 官方 |
| HTTP/2 | 需要 Hypercorn | ✅ 原生支持 |
| 推薦場景 | 生產環境首選 | 需要 Channels 全功能 |
推薦:
- 一般場景:Uvicorn ✅
- 使用 Django Channels:Daphne ✅
- 需要 HTTP/2/3:Hypercorn ✅
Q5: ASGI 一定比 WSGI 快嗎?
答案:
不一定! 取決於應用類型:
ASGI 更快(I/O 密集型):
async def async_view(request):
# 大量 I/O 操作
data1 = await fetch_api1() # 100ms
data2 = await fetch_api2() # 100ms
data3 = await fetch_api3() # 100ms
# 並行執行,總耗時 100msWSGI 可能更快(CPU 密集型):
def sync_view(request):
# CPU 密集型計算
result = complex_calculation() # 1 秒
# WSGI: 1 秒
# ASGI: 1 秒 + 事件循環開銷,而且阻塞其他協程結論:
- ✅ I/O 密集型:ASGI 可快 10-50 倍
- ❌ CPU 密集型:ASGI 可能更慢
- 🟡 混合型:需要測試評估
9. 總結
9.1 核心要點
WSGI(2003):
- 同步、阻塞、成熟穩定
- 適合傳統 Web 應用和 CPU 密集型任務
- 並發受限於 Worker 數量
ASGI(2016):
- 異步、非阻塞、高並發
- 適合 I/O 密集型和實時應用
- 支持 WebSocket 和 HTTP/2
選擇標準:
- 需要 WebSocket?→ ASGI
- I/O 密集型?→ ASGI
- CPU 密集型?→ WSGI
- 追求穩定?→ WSGI
Django 支持:
- Django 3.0+ 同時支持兩者
- 可以混合部署
- 異步 ORM 需要 Django 4.1+
9.2 快速對比表
| 場景 | WSGI | ASGI | 推薦 |
|---|---|---|---|
| 傳統 CRUD 應用 | ✅ | ✅ | WSGI |
| API 密集型 | 🟡 | ✅ | ASGI |
| WebSocket | ❌ | ✅ | ASGI |
| CPU 密集型 | ✅ | ❌ | WSGI |
| 高並發(> 1000 QPS) | 🟡 | ✅ | ASGI |
| 團隊經驗不足 | ✅ | 🟡 | WSGI |
參考資料
官方文檔:
ASGI 服務器:
深入閱讀:
下一篇預告:12-2. Django Async Views - 深入理解 Django 異步視圖的實現與最佳實踐