1.0.0 Примечания к выпуску

Channels 1.0.0 объединяет ряд изменений в дизайне, включая некоторые ломающие изменения, в нашем первом полностью стабильном релизе, а также выводит код привязки данных из альфа-фазы. Он был выпущен 2017/01/08.

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

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

Вы должны также обновить Daphne как минимум до версии 1.0.0, чтобы этот выпуск Channels работал корректно.

Основные характеристики

Channels 1.0 представляет несколько новых основных функций.

Поток принятия/отклонения WebSocket

Вместо того, чтобы быть немедленно принятыми, WebSockets теперь приостанавливают соединение во время квитирования, пока они передают сообщение на websocket.connect, и ваше приложение должно либо принять, либо отклонить соединение, прежде чем квитирование будет завершено и сообщения могут быть получены.

Вы должны обновить Daphne по крайней мере до версии 1.0.0, чтобы это работало правильно.

Это имеет несколько преимуществ:

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

  • В сочетании с атомарностью потребителей (см. ниже) это означает, что больше нет необходимости в старом режиме «легкого упорядочивания», так как потребитель connect должен завершить работу и принять сокет, прежде чем любые сообщения могут быть получены и переданы на websocket.receive.

  • Любое сообщение send, отправленное на WebSocket, будет неявно принимать соединение, что означает, что изменения нужны только ограниченному набору потребителей connect (см. ниже раздел «Обратные несовместимые изменения»).

Атомарность потребителя

Потребители теперь будут буферизировать сообщения, которые вы пытаетесь отправить, до завершения работы потребителя, а затем отправлять их после его выхода и выполнения исходящей части любых декораторов (даже если возникнет исключение).

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

Если вы хотите отправлять сообщения сразу, а не в конце работы потребителя, вы можете сделать это, передав аргумент immediately:

Channel("thumbnailing-tasks").send({"id": 34245}, immediately=True)

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

Пересмотр группы/действия привязки данных

Ранее подклассы привязки к базе данных должны были реализовывать group_names(instance, action) для возврата того, в какие группы отправлять изменение экземпляра типа action. Это имело недостатки, в частности, когда то, что на самом деле было просто изменением рассматриваемого экземпляра, изменяло его статус разрешения, чтобы больше клиентов могли его видеть; для этих клиентов он должен был быть «создан».

Теперь Channels просто вызывает group_names(instance), и вы должны вернуть, какие группы могут видеть экземпляр в текущий момент времени, учитывая переданный вам экземпляр. Channels будет фактически вызывать метод до и после изменений, сравнивая группы, которые вы передали, и посылая сообщения о создании, обновлении или удалении клиентам соответствующим образом.

Существующий код привязки базы данных необходимо будет адаптировать; подробнее см. в разделе «Несовместимые изменения в обратную сторону».

Капитальный ремонт демультиплексора

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

Они также теперь пересылают все сообщения websocket.connect и << 1 >>> всем своим субпотребителям, поэтому гораздо проще компоновать вещи вместе из кода, который также работает вне контекста мультиплексирования.

Подробнее читайте в обновленной документации /generic.

Сервер задержки

Встроенный сервер задержки, запускаемый с помощью manage.py rundelay, теперь поставляется, если вы хотите его использовать. Он требует некоторой дополнительной начальной настройки и использует базу данных для хранения данных; более подробную информацию смотрите в /delay.

Незначительные изменения

  • Сериализаторы теперь могут указывать поля как __all__ для автоматического включения всех полей, и exclude для удаления некоторых ненужных полей.

  • runserver уважает FORCE_SCRIPT_NAME

  • Websockets теперь можно закрыть с помощью определенного кода, вызвав close(status=4000)

  • enforce_ordering больше не имеет режима slight (из-за изменения потока приема), и более эффективно сохраняет сессию.

  • runserver соответствует --nothreading и запускает только одного рабочего, принимает опцию --http-timeout, если вы хотите переопределить ее с 60 по умолчанию,

  • Новый декоратор @channel_and_http_session выводит HTTP-сессию из сессии канала, если вы хотите получить к ней доступ внутри потребителей receive.

  • Потоковые ответы больше не имеют шансов на кэширование.

  • request.META['SERVER_PORT'] теперь всегда является строкой.

  • http.disconnect теперь имеет клавишу path, чтобы вы могли его маршрутизировать.

  • Тестовый клиент теперь имеет метод send_and_consume.

Обратные несовместимые изменения

Подключение потребителей

Если у вас есть пользовательский потребитель для websocket.connect, вы должны убедиться, что он либо:

  • Отправляет по крайней мере одно сообщение на reply_channel, которое генерирует фрейм WebSocket (установлено либо bytes, либо << 2 >>>), либо напрямую, либо через группу.

  • Посылает сообщение на reply_channel, которое равно {"accept": True}, чтобы принять соединение без отправки данных.

  • Посылает сообщение на reply_channel, которое является {"close": True}, чтобы отклонить соединение в середине квитирования.

Многие потребители уже делают первое, но если ваш потребитель connect ничего не посылает, вы ДОЛЖНЫ теперь послать сообщение accept, иначе сокет навсегда останется в фазе квитирования, и вы никогда не получите никаких сообщений.

Все встроенные потребители Channels (например, в общих потребителях) были модернизированы для этого.

Вы должны обновить Daphne по крайней мере до версии 1.0.0, чтобы это работало правильно.

Привязка базы данных group_names

Если у вас есть подклассы привязки к базе данных, то вы уже реализовали group_names(instance, action), который возвращает группы для использования на основе предоставленного экземпляра и действия.

Теперь, вместо этого, вы должны реализовать group_names(instance), которая возвращает группы, которые могут видеть экземпляр в том виде, в котором он представлен для вас; результаты действий будут отработаны для вас. Например, если вы хотите показывать объекты, помеченные как «admin_only», только администраторам, а объекты без этой пометки - всем, раньше вы должны были сделать следующее:

def group_names(self, instance, action):
    if instance.admin_only:
        return ["admins"]
    else:
        return ["admins", "non-admins"]

Поскольку вы ничего не сделали на основе action (а если бы сделали, то получили бы неполные сообщения, отсюда и это изменение дизайна), вы можете просто изменить сигнатуру метода следующим образом:

def group_names(self, instance):
    if instance.admin_only:
        return ["admins"]
    else:
        return ["admins", "non-admins"]

Теперь, когда объект обновляется, чтобы иметь admin_only = True, клиенты в группе non-admins получат сообщение delete, а клиенты в группе admins получат сообщение update.

Демультиплексоры

Демультиплексоры перешли от использования диктанта mapping, который отображал имена потоков на каналы, к использованию диктанта consumers, который отображает имена потоков непосредственно на классы потребителей.

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

Кроме того, демультиплексор теперь пересылает сообщения так, как они выглядели бы при прямом соединении, то есть там, где раньше вы получали декодированный объект, теперь вы получите правильно отформатированное сообщение websocket.receive с содержимым в виде ключа text, закодированного в JSON. Теперь вам также придется обрабатывать сообщения websocket.connect и << 3 >>>.

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

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