WSGI vs ASGI — практичний розбір¶
WSGI достатній для sync HTTP Django. ASGI потрібний для WebSocket і async runtime. У
notes_chat_appобидва протоколи існують паралельно в одному процесі Uvicorn.
Коротка різниця¶
| WSGI | ASGI | |
|---|---|---|
| Стандарт | PEP 3333 (2010) | PEP 3333 наступник (2019) |
| Протоколи | Тільки HTTP | HTTP + WebSocket + HTTP/2 |
| Виконання | Синхронне (блокуючий I/O) | Асинхронне (asyncio event loop) |
| Файл у проєкті | notes_project/wsgi.py |
notes_project/asgi.py |
| Сервер | Gunicorn, uWSGI | Uvicorn, Daphne |
| Підходить для | CRUD, форми, авторизація | + WebSocket, real-time, streaming |
Де знайти у проєкті¶
notes_project/wsgi.py¶
# notes_project/wsgi.py
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'notes_project.settings')
application = get_wsgi_application()
# ↑ Стандартний Django WSGI callable
# Використовується якщо запускати через Gunicorn:
# gunicorn notes_project.wsgi:application
#
# У notes_chat_app цей файл НЕ використовується в production —
# ми використовуємо asgi.py і Uvicorn.
# Файл залишений для сумісності (деякі платформи шукають wsgi.py)
Що в ньому: один рядок реального коду — application = get_wsgi_application().
Це callable, який приймає environ dict і start_response функцію. Ніколи не чіпати без потреби.
notes_project/asgi.py¶
# notes_project/asgi.py
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'notes_project.settings')
# CRITICAL: ПЕРШИЙ рядок до будь-яких імпортів з notes_app
django_asgi_app = get_asgi_application()
# Після get_asgi_application() — безпечно імпортувати models, consumers
from notes_project.routing import websocket_urlpatterns
application = ProtocolTypeRouter({
"http": django_asgi_app, # HTTP → звичайний Django
"websocket": AuthMiddlewareStack(
URLRouter(websocket_urlpatterns) # WebSocket → Consumer
),
})
Що тут змінювати:
- Додати новий протокол (наприклад, "lifespan") — дуже рідко
- Змінити AuthMiddlewareStack на власний middleware — якщо потрібна кастомна авторизація WS
Що НЕ змінювати:
- Порядок: get_asgi_application() завжди першим
- ProtocolTypeRouter — структура обов'язкова для Channels
notes_project/routing.py¶
# notes_project/routing.py
from django.urls import re_path
from notes_app import consumers
websocket_urlpatterns = [
re_path(
r'^ws/groups/(?P<group_pk>\d+)/chat/$',
consumers.GroupChatConsumer.as_asgi(),
),
]
Що тут змінювати:
- Додати новий WebSocket URL → новий рядок у websocket_urlpatterns
- Змінити URL pattern → відредагувати regex
- Підключити новий Consumer → consumers.NewConsumer.as_asgi()
Не плутати з notes_app/urls.py — той для HTTP URL, цей для WebSocket URL.
notes_project/settings.py — ключові параметри¶
# settings.py — що стосується ASGI:
INSTALLED_APPS = [
'daphne', # ← ПЕРШИЙ: override runserver → ASGI
'channels', # ← Django Channels framework
# ...
]
# Вказує який ASGI callable використовувати:
ASGI_APPLICATION = 'notes_project.asgi.application'
# ↑ Змінюй тільки якщо перейменував asgi.py або переніс у інший пакет
# Channel layer:
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [{
"address": os.environ.get('REDIS_URL'),
"socket_timeout": None,
"health_check_interval": 30,
}]
}
}
}
# ↑ Змінюй: адресу Redis, параметри timeout
Як це запускається у Docker¶
entrypoint.sh:
exec python -m uvicorn notes_project.asgi:application \
--host 0.0.0.0 --port 8001
Uvicorn:
→ завантажує notes_project/asgi.py
→ отримує application = ProtocolTypeRouter({...})
При HTTP запиті від nginx:
ProtocolTypeRouter: protocol = "http"
→ передає до django_asgi_app (views, urls, templates)
При WebSocket від nginx (Upgrade: websocket):
ProtocolTypeRouter: protocol = "websocket"
→ AuthMiddlewareStack → URLRouter → GroupChatConsumer
Коли що змінювати¶
| Задача | Файл | Що робити |
|---|---|---|
| Додати новий HTTP URL | notes_app/urls.py |
path(...) у urlpatterns |
| Додати новий WebSocket URL | notes_project/routing.py |
re_path(...) у websocket_urlpatterns |
| Додати новий Consumer | notes_app/consumers.py |
Новий клас + routing.py |
| Змінити Redis конфіг | notes_project/settings.py |
CHANNEL_LAYERS["CONFIG"] |
| Змінити middleware для WS | notes_project/asgi.py |
Змінити AuthMiddlewareStack(...) |
| Додати lifespan events | notes_project/asgi.py |
"lifespan": ... у ProtocolTypeRouter |
| Змінити WSGI (для gunicorn) | notes_project/wsgi.py |
Рідко потрібно |
Практичне завдання¶
# Запусти стек:
docker compose up -d
# 1. Переконайся що ASGI працює:
docker compose logs web | grep "Uvicorn running"
# 2. Відкрий чат у DevTools → Network → WS
# Переконайся що WS з'єднання встановлюється (101 Switching Protocols)
# 3. Переглянь asgi.py та routing.py:
docker compose exec web cat notes_project/asgi.py
docker compose exec web cat notes_project/routing.py
Далі¶
Детальніше про Channels, async consumers і WebSocket: - 07 — Async та WebSocket чат - Async and Realtime