Ответ
Для реализации WebSocket в Django используется библиотека Django Channels, которая расширяет фреймворк для работы с асинхронными протоколами поверх стандарта ASGI (Asynchronous Server Gateway Interface).
Это позволяет обрабатывать долгоживущие соединения, такие как WebSocket, в то время как стандартные HTTP-запросы по-прежнему обрабатываются синхронным кодом Django.
Основные компоненты:
- ASGI-сервер: Вместо WSGI (Gunicorn) используется ASGI-сервер, например, Daphne или Uvicorn.
- Consumers: Аналоги
viewsдля WebSocket. Это классы, которые обрабатывают события жизненного цикла соединения (connect,disconnect,receive). - Channel Layers: Механизм для коммуникации между различными экземплярами приложения. Обычно реализуется через брокер сообщений, например, Redis.
Пример простого консьюмера чата:
# chat/consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class ChatConsumer(AsyncWebsocketConsumer):
# Вызывается при установке соединения
async def connect(self):
self.room_group_name = 'chat_public'
# Присоединение к группе комнаты
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
)
# Вызывается при получении сообщения от WebSocket
async def receive(self, text_data):
# Отправка сообщения в группу комнаты
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message', # Вызывает метод chat_message
'message': text_data
}
)
# Получение сообщения из группы комнаты и отправка его клиенту
async def chat_message(self, event):
message = event['message']
await self.send(text_data=json.dumps({
'message': message
}))
Для работы этого примера необходима настройка роутинга в asgi.py и установка channel_layers в settings.py.
Ответ 18+ 🔞
Слушай, а вот представь себе, блядь, классический Django — это как старый добрый почтальон. Пришёл, письмо вручил, хуяк — и свалил. А тебе вдруг захотелось, чтобы он не просто письмо кинул, а сел на лавочку, чайку попил и болтал с тобой часами, пока ты ему свои анекдоты рассказываешь. Это ж, ёпта, WebSocket!
Так вот, чтобы этот почтальон не сдох от скуки и не сбежал, нам нужен Django Channels. Это такая надстройка, которая превращает нашего синхронного почтальона в асинхронного болтуна, способного держать связь долго и счастливо.
Из чего эта магия состоит, блядь:
- ASGI-сервер (Daphne или Uvicorn). Это уже не старый дед Gunicorn, который только на один запрос за раз способен. Это как замена конторы на целый колл-центр, где операторы (воркеры) могут одновременно и письма разбирать, и по телефону трепаться.
- Consumers. Это, сука, самые главные болтуны. Как
viewsдля обычных запросов, только для WebSocket. Они ловят события: «клиент подключился», «клиент отвалился», «клиент что-то ляпнул». И на каждое у них есть свой ответ. - Channel Layers. А это, блядь, самое важное для масштаба. Представь, у тебя не один болтун, а целая толпа на разных серверах. Как им общаться между собой? Через брокера! Обычно это Redis — такая общая почтовая комната, куда все кидают записки, а кто надо — тот и читает.
Смотри, как это выглядит в коде, простейший чат:
# chat/consumers.py
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class ChatConsumer(AsyncWebsocketConsumer):
# Вот тут клиент только-только руку протянул для рукопожатия
async def connect(self):
self.room_group_name = 'chat_public' # Название нашей общей палаты, куда все заходят
# Записываем чувака в список участников этой палаты
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):
# Берём это сообщение и кричим на всю нашу общую палату!
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message', # Это команда: "Эй, все, выполните метод chat_message!"
'message': text_data # А это само послание
}
)
# Этот метод как раз и вызывается для ВСЕХ, кто в палате
async def chat_message(self, event):
message = event['message'] # Вытаскиваем текст
# И каждому отдельному участнику отправляем его обратно
await self.send(text_data=json.dumps({
'message': message
}))
Ну и понятное дело, что одного этого консьюмера мало. Надо ещё в asgi.py маршруты прописать, чтобы Django понимал, куда какие WebSocket-запросы слать, и в settings.py настроить этого самого channel_layers (обычно на Redis), иначе все твои болтуны будут жить в разных мирах и общаться не смогут. Но это уже технические детали, ёпта. Главную суть ты уловил?