DEV Community

Danil Poletavkin
Danil Poletavkin

Posted on

Django. Работа через WebSocket. Библиотека Channels

Ссылка на документацию https://channels.readthedocs.io/

Ссылка на проект github https://github.com/django/channels

Библиотека позволяет выполнять соединение с клиентской стороной как через http, так и через websocket. Подключая библиотеку к Django соединения будут выполняться через сервер-стандарт asgi, так как библиотека переопределит стандартное поведение Django. В остальном же функциональность остаётся та же самая, как обычно, через http можно вызывать контроллеры из файла views.py.

Классы-Потребители (Consumers)

Это специальный класс — потребитель сообщений. Принято располагать эти классы в модуле consumers.py.

Эти классы обрабатывают сообщения по протоколам отличным от http, например для работы с websocket класс создаётся следующим образом:

from channels.generic.websocket import WebsocketConsumer

class SimpleWsConsumer(WebsocketConsumer):
    pass
Enter fullscreen mode Exit fullscreen mode

В отличие от стандартной конфигурации при работе через http, когда маршруты располагаются в модуле urls.py при работе через websocket маршруты принято располагать в модуле routing.py

from django.urls import re_path
from . import consumers

websocket_urlpatterns=[
    re_path(r’ws/chat/(?P<room_name>\w+)/$’, consumers.SimpleWsConsumer.as_asgi()),
]
Enter fullscreen mode Exit fullscreen mode

Как видим, у класса-контроллера вместо обычного вызова as_view() вызывается метод as_asgi(), в остальном модуль маршрутов похож на стандартный urls.py

Подключаются же такие маршруты в модуле asgi.py

import os

from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from django.core.asgi import get_asgi_application
from chat import routing # Импортируется модуль routing.py, где как раз и настроены маршруты websocket

os.environ.setdefault('DJANGO_SETTINGS_MODULE','mysite.settings')
application=ProtocolTypeRouter({
    "http":get_asgi_application(),
    "websocket":AllowedHostsOriginValidator(AuthMiddlewareStack(URLRouter(routing.websockets_urlpatterns)) # Передаётся список маршрутов из модуля routing.py)
})
Enter fullscreen mode Exit fullscreen mode

Подключение коммуникационной системы потребителей. Слой Каналов (Channel layer)

Канал — это как путь, через который направляются сообщения. Для каждой вкладки веб-обозревателя создаётся свой канал. Каждому потребителю по умолчанию назначается уникальное имя канала channel_name, то есть если создать 10 вкладок (или приложение используют 10 разных пользователей), то будет создано 10 каналов. Наверное можно думать о канале как о канале соединения websocket. Каналы могут объединяться в группы, и тогда каналы, принадлежащие одной группе могут общаться между собой

Группа — это группа связанных каланов. Имея имя группы можно отправлять сообщения через все каналы этой группы. И, насколько понимаю, для каждого потребителя создаётся свой собственный канал с уникальным именем. Получается, что каналы объединяются в группы вместе со своими потребителями.

То есть можно сказать, что канал — это путь к потребителю, по которому идут сообщения, а потребитель — конечная точка для этих сообщений. Потребитель может принять сообщение, обработать его и отправить ответ.

Коммуникационная система работает через redis — нужно запустить redis и добавить пакет channels_redis

pip install channels_redis

И в модуле настроек settings.py коммуникационную систему нужно сконфигурировать

ASGI_APPLICATION='mysite.asgi.application'

CHANNEL_LAYERS={
    'default':{
        'BACKEND':'channels_redis.core.RedisChannelLayer',
        'CONFIG':{
            'hosts':[
                ('127.0.0.1',6379)
            ]
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Добавление потребителя в группу

Потребителя можно добавить в группу методом channel_layer.group_add

import json

from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer

class SimpleWsConsumer(WebsocketConsumer):
    def connect(self):
        self.room_name=self.scope['url_route'][^1]['kwargs']['room_name']
        self.room_group_name='chat_%s' % self.room_name
        # Добавление потребителя в группу
        async_to_sync(self.channel_layer.group_add)[^2](
            self.room_group_name, self.channel_name # Параметры метода channel_layer.group_add
        )
    self.accept() # Принять соединение

Enter fullscreen mode Exit fullscreen mode

Top comments (0)