Вступление

Добро пожаловать в Channels!

Channels обеспечивают встроенную поддержку асинхронного представления Django, позволяя проектам Django обрабатывать не только HTTP, но и протоколы, требующие длительных подключений - WebSockets, MQTT, чат-боты, любительское радио и многое другое.

Это достигается при сохранении синхронной и простой в использовании природы Django, что позволяет вам выбирать способ написания кода - синхронно в стиле, подобном представлениям Django, полностью асинхронным или сочетанием обоих. Вдобавок к этому, он обеспечивает интеграцию с системой аутентификации Django, системой сеансов и многим другим, что упрощает расширение вашего HTTP-проекта на другие протоколы.

Channels также объединяют эту управляемую событиями архитектуру со слоями каналов, системой, которая позволяет вам легко обмениваться данными между процессами и разделять ваш проект на разные процессы.

Если вы еще не установили Channels, вы можете сначала прочитать Установка, чтобы установить его. Это введение не является прямым руководством, но вы должны иметь возможность использовать его, чтобы следовать и вносить изменения в существующий проект Django, если хотите.

Черепахи в порядке убывания

Каналы работают по принципу «все черепахи внизу» - у нас есть единое представление о том, что такое «приложение» для каналов, и даже простейший из потребителей (эквивалент представлений Django) является полностью допустимым приложением ASGI, которое можно запускать самостоятельно.

Примечание

ASGI - это название спецификации асинхронного сервера, на котором построен Channels. Как и WSGI, он предназначен для того, чтобы вы могли выбирать между различными серверами и средами, а не быть привязанным к Channels и нашему серверу Daphne. Вы можете узнать больше на http://asgi.readthedocs.io

Каналы дают вам инструменты для написания основных потребителей - отдельных частей, которые могут обрабатывать обмен сообщениями в чате или уведомления - и связывать их вместе с маршрутизацией URL, обнаружением протокола и другими удобными вещами для создания полноценного приложения.

Мы рассматриваем HTTP и существующее приложение Django как часть единого целого. Традиционные представления Django все еще существуют с Channels и по-прежнему могут использоваться - с встроенной поддержкой ASGI в Django или с версией, предоставленной Channels для Django 2.2, - но теперь вы также можете написать собственную обработку длинного опроса HTTP или получателей WebSocket и разместить этот код рядом с вашим существующим кодом. URL-маршрутизация, промежуточное ПО - все это просто приложения ASGI.

Мы считаем, что вам нужна возможность использовать безопасные синхронные методы, такие как представления Django, для большей части кода, но у вас есть возможность перейти к более прямому асинхронному интерфейсу для сложных задач.

Области применения и события

Channels и ASGI разделяют входящие соединения на два компонента: scope (область) и серия events (событий).

Scope - это набор сведений об одном входящем соединении - например, путь, с которого был сделан веб-запрос, или исходный IP-адрес WebSocket’а, или пользователь, отправляющий сообщение в чат-бот - и сохраняется в течение всего соединения.

Для HTTP область действия длится всего один запрос. Для WebSockets он действует в течение всего времени существования сокета (но изменяется, если сокет закрывается и повторно подключается). Для других протоколов это зависит от того, как написана спецификация протокола ASGI; например, вполне вероятно, что протокол чат-бота оставит одну область открытой для всего разговора пользователя с ботом, даже если базовый протокол чата не имеет состояния.

Во время существования этой области действия происходит серия событий. Они представляют пользовательские взаимодействия - например, создание HTTP-запроса или отправка фрейма WebSocket. Ваши каналы или приложения ASGI будут созданы один раз для каждой области, а затем будут переданы потоки событий, происходящих в этой области, чтобы решить, что делать с ними.

Пример с HTTP:

  • Пользователь делает HTTP-запрос.

  • Мы открываем новую область типа http с подробностями пути запроса, метода, заголовков и т.д.

  • Мы отправляем событие http.request с содержимым тела HTTP

  • Приложение Channels или ASGI обрабатывает это и генерирует событие http.response для отправки обратно в браузер и закрытия соединения.

  • HTTP-запрос/ответ завершен, а область действия уничтожена.

Пример с чат-ботом:

  • Пользователь отправляет первое сообщение в чат-бот.

  • Это открывает область, содержащую имя пользователя, выбранное имя и идентификатор пользователя.

  • Приложение получает событие chat.received_message с текстом события. Он не должен отвечать, но может отправить одно, два или более других сообщений чата обратно как chat.send_message события, если он этого хочет.

  • Пользователь отправляет больше сообщений чат-боту и генерирует больше событий chat.received_message.

  • По истечении времени ожидания или после перезапуска процесса приложения область действия закрывается.

В течение времени существования области - будь то чат, HTTP-запрос, соединение с сокетом или что-то еще - у вас будет один экземпляр приложения, обрабатывающий все события из него, и вы также можете сохранять вещи в экземпляре приложения. Вы можете написать отдельное приложение ASGI, если хотите, но каналы дают вам простую в использовании абстракцию над ними, называемую потребителями.

Что такое потребитель?

Потребитель - это основная единица кода Channels. Мы называем это потребителем, поскольку он потребляет события, но вы можете думать о нем как о своем собственном крошечном приложении. Когда приходит запрос или новый сокет, Channels будет следовать таблице маршрутизации - мы рассмотрим это чуть-чуть - найдем подходящего потребителя для этого входящего соединения и запустим его копию.

Это означает, что, в отличие от представлений Django, потребители долгоработающие. Они также могут быть краткосрочными - в конце концов, HTTP-запросы также могут обслуживаться потребителями - но они основаны на идее жить некоторое время (они живут в течение scope, как мы описали выше ).

Обычный потребитель выглядит так:

class ChatConsumer(WebsocketConsumer):

    def connect(self):
        self.username = "Anonymous"
        self.accept()
        self.send(text_data="[Welcome %s!]" % self.username)

    def receive(self, *, text_data):
        if text_data.startswith("/name"):
            self.username = text_data[5:].strip()
            self.send(text_data="[set your username to %s]" % self.username)
        else:
            self.send(text_data=self.username + ": " + text_data)

    def disconnect(self, message):
        pass

Каждый отдельный протокол имеет разные виды событий, которые происходят, и каждый тип представлен своим методом. Вы пишете код, который обрабатывает каждое событие, а Channels позаботится о их планировании и параллельном запуске.

Внизу, Channels работает в полностью асинхронном цикле событий, и если вы напишете код, подобный приведенному выше, он будет вызываться в синхронном потоке. Это означает, что вы можете безопасно выполнять операции блокировки, такие как вызов Django ORM:

class LogConsumer(WebsocketConsumer):

    def connect(self, message):
        Log.objects.create(
            type="connected",
            client=self.scope["client"],
        )

Однако, если вам нужен больший контроль и вы готовы работать только с асинхронными функциями, вы можете написать полностью асинхронных потребителей:

class PingConsumer(AsyncConsumer):
    async def websocket_connect(self, message):
        await self.send({
            "type": "websocket.accept",
        })

    async def websocket_receive(self, message):
        await asyncio.sleep(1)
        await self.send({
            "type": "websocket.send",
            "text": "pong",
        })

Вы можете прочитать больше о потребителях в Потребители.

Маршрутизация и несколько протоколов

Вы можете объединить несколько потребителей (которые, помните, являются их собственными приложениями ASGI) в одно более крупное приложение, которое представляет ваш проект, используя маршрутизацию:

application = URLRouter([
    url(r"^chat/admin/$", AdminChatConsumer.as_asgi()),
    url(r"^chat/$", PublicChatConsumer.as_asgi(),
])

Каналы не просто построены вокруг мира HTTP и WebSockets - они также позволяют встроить любой протокол в среду Django, создав сервер, который отображает эти протоколы в аналогичный набор событий. Например, вы можете создать чат-бота в похожем стиле:

class ChattyBotConsumer(SyncConsumer):

    def telegram_message(self, message):
        """
        Simple echo handler for telegram messages in any chat.
        """
        self.send({
            "type": "telegram.message",
            "text": "You said: %s" % message["text"],
        })

А затем используйте другой маршрутизатор, чтобы один проект мог обслуживать как WebSockets, так и запросы чата:

application = ProtocolTypeRouter({

    "websocket": URLRouter([
        url(r"^chat/admin/$", AdminChatConsumer.as_asgi()),
        url(r"^chat/$", PublicChatConsumer.as_asgi()),
    ]),

    "telegram": ChattyBotConsumer.as_asgi(),
})

Цель Channels - позволить вам создавать свои проекты Django для работы с любым протоколом или транспортом, с которыми вы можете столкнуться в современной сети, и в то же время позволяя вам работать со знакомыми компонентами и стилем кодирования, к которым вы привыкли.

Для получения дополнительной информации о маршрутизации протокола см. Маршрутизация.

Межпроцессное взаимодействие

Подобно стандартному серверу WSGI, код приложения, который обрабатывает события протокола, выполняется внутри самого процесса сервера - например, код обработки WebSocket выполняется внутри процесса сервера WebSocket.

Каждый сокет или соединение с вашим общим приложением обрабатывается экземпляром приложения внутри одного из этих серверов. Их вызывают, и они могут напрямую отправлять данные клиенту.

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

Вы можете сделать это путем опроса базы данных, но Channels вводит идею уровня канала, низкоуровневой абстракции вокруг набора транспортов, которые позволяют отправлять информацию между различными процессами. Каждый экземпляр приложения имеет уникальное имя канала и может присоединяться к группам, что позволяет осуществлять как двухточечный, так и широковещательный обмен сообщениями.

Примечание

Слои каналов являются необязательной частью Channels и могут быть отключены, если вы хотите (установив для параметра CHANNEL_LAYERS пустое значение).

(вставьте пример кросс-процесса здесь)

Вы также можете отправлять сообщения выделенному процессу, который прослушивает собственное фиксированное имя канала:

# In a consumer
self.channel_layer.send(
    "myproject.thumbnail_notifications",
    {
        "type": "thumbnail.generate",
        "id": 90902949,
    },
)

Вы можете прочитать больше о слоях каналов в Слои канала.

Интеграция с Django

Channels поставляются с простой поддержкой общих функций Django, таких как сеансы и аутентификация. Вы можете объединить аутентификацию с вашими представлениями WebSocket, просто добавив вокруг них подходящее промежуточное ПО:

from django.urls import re_path
from django.core.asgi import get_asgi_application

from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack

application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": AuthMiddlewareStack(
        URLRouter([
            re_path(r"^front(end)/$", consumers.AsyncChatConsumer.as_asgi()),
        ])
    ),
})

Для получения дополнительной информации смотрите Сессии и Аутентификация.

Вернуться на верх