Создание движений w/websockets и python/django (/twisted?)

Интересная часть websockets отправляет по существу незатребованный контент с сервера на браузер?

Хорошо, я использую django-websocket от Gregor Müllegger. Это действительно замечательная ранняя трещина при создании веб-сайтов в Django.

Я совершил "привет мир". Как это работает: когда запрос представляет собой websocket, объект объекта websocket добавляется к объекту запроса. Таким образом, я могу, с точки зрения интерпретации websocket, сделать что-то вроде:

request.websocket.send('We are the knights who say ni!')

Это прекрасно работает. Я получаю сообщение в браузере как прелесть.

Но что, если я хочу сделать это, не вызывая запрос из браузера?

ОК, поэтому сначала я сохраняю websocket в словаре сеанса:

request.session['websocket'] = request.websocket

Затем в оболочке я иду и захватываю сессию с помощью сеансового ключа. Разумеется, в словаре сеансов есть объект websocket. Happy!

Однако, когда я пытаюсь сделать:

>>> session.get_decoded()['websocket'].send('With a herring!')

Я получаю:

Traceback (most recent call last):
File "<console>", line 1, in <module>
error: [Errno 9] Bad file descriptor

Сад.: - (

ОК, поэтому я почти ничего не знаю о сокетах, но я знаю достаточно, чтобы обнюхать в отладчике, и вот, я вижу, что сокет в моем отладчике (который привязан к подлинному веб-сайту из запрос) имеет fd = 6, а тот, который я захватил из сохраненного сеансом websocket, имеет fd = -1.

Может ли сокет-ориентированный человек помочь мне сортировать этот материал?

Ответ 1

Я автор django-websocket. Я не являюсь экспертом в области веб-сайтов и сетей, однако, я думаю, что у меня есть достойное понимание того, что происходит. Извините за подробные сведения. Даже если большая часть ответа не зависит от вашего вопроса, это может помочь вам в каком-то другом месте.: -)


Как работают веб-сайты

Позвольте мне вкратце объяснить, что такое websocket. Веб-сайт запускается как нечто похожее на простой HTTP-запрос, установленный в браузере. Он указывает через HTTP-заголовок, что он хочет "обновить" протокол как websocket вместо HTTP-запроса. Если сервер поддерживает веб-порты, он соглашается на рукопожатие, и оба - сервер и клиент - теперь знают, что они будут использовать установленный tcp-сокет, ранее используемый для HTTP-запроса, в качестве соединения для обмена сообщениями веб-рассылки.

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

Как django-websocket злоупотребляет средой запросов python wsgi для захвата сокета

Теперь давайте подробно рассмотрим, как django-websocket реализует "обновление" HTTP-запроса в цикле запросов-ответа django.

Django обычно использует спецификацию WSGI для общения с веб-сервером, таким как apache или gunicorn и т.д. Эта спецификация была разработана только с очень ограниченной коммуникационной моделью HTTP в виду. Предполагается, что он получает HTTP-запрос (только входящие данные) и возвращает ответ (только исходящие данные). Это делает сложным заставить django в концепцию websocket, где допускается двунаправленная связь.

Что я делаю в django-websocket для этого, так это то, что я очень глубоко вникаю в внутренние объекты WSGI и объекта запроса django для извлечения подкладочного сокета. Этот tcp-сокет затем используется для обработки обновления HTTP-запроса непосредственно в экземпляре websocket.

Теперь к вашему оригинальному вопросу...

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

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

После возврата из представления веб-камера автоматически закрывается. Это делается по какой-то причине: мы не хотим, чтобы сокет был открыт для undefined времени и полагался на клиента (браузер), чтобы закрыть его.

Вот почему вы не можете получить доступ к веб-узлу с помощью django-websocket вне вашего представления. Тогда дескриптор файла, конечно, установлен в -1, что указывает на то, что он уже закрыт.

Отказ

Я объяснил выше, что я копаю в окружающей среде django, чтобы получить как-то - очень хакерским способом - доступ к подстилающей розетке. Это очень хрупко, а также не должно работать, поскольку WSGI не предназначен для этого! Я также объяснил выше, что websocket закрывается после того, как представление заканчивается - однако после закрытия websocket (И закрыл tcp-сокет) реализация django WSGI пытается отправить HTTP-ответ - он не знает о веб-сайтах и ​​считает, что он находится в обычный цикл HTTP-запроса-ответа. Но сокет уже закрыт, и отправка не удастся. Обычно это вызывает исключение в django.

Это не повлияло на мои тесты с сервером разработки. Браузер никогда не заметит (вы знаете.. сокет уже закрыт;-) - но повышение необработанной ошибки в каждом запросе - не очень хорошая концепция и может протекать в памяти, не справляется с отключением подключения к базе данных правильно и многие вещи athor который в какой-то момент сломается, если вы используете django-websocket для более чем экспериментирования.

Вот почему я бы действительно посоветовал вам не использовать websockets с django. Он не работает по дизайну. Django и особенно WSGI нуждались бы в капитальном ремонте для решения этих проблем (см. это обсуждение для веб-узлов и WSGI). С тех пор я бы предложил использовать что-то вроде eventlet, Eventlet имеет рабочую реализацию websocket (я заимствовал некоторый код из eventlet для начальной версии django-websocket), и с его простого кода на Python вы можете импортировать свои модели и все остальное из django. Единственным недостатком является то, что вам нужен второй веб-сервер, работающий только для обработки веб-сайтов.

Ответ 2

Как отметил Грегор Мюллегергер, Websockets не могут быть надлежащим образом обработаны WSGI, потому что этот протокол никогда не был разработан для обработки такой функции. uWSGI, начиная с версии 1.9.11, может обрабатывать веб-узлы из коробки. Здесь uWSGI обменивается данными с сервером приложений, используя протокол HTTP, а не протокол WSGI. Таким образом, сервер, написанный таким образом, может обрабатывать внутренности протокола и поддерживать соединение открытым в течение длительного периода времени. Отсутствие длинных живых подключений, обрабатываемых представлением Django, также не является хорошей идеей, поскольку они затем блокируют рабочий поток, который является ограниченным ресурсом.

Основная цель Websockets заключается в том, чтобы сервер нажимал сообщения клиенту асинхронным способом. Это может быть представление Django, инициированное другими браузерами (например, чат-клиенты, многопользовательские игры) или событие, инициированное, например, django-celery (например, спортивные результаты). Поэтому для этих служб Django принципиально важно использовать очередь сообщений для передачи сообщений клиенту.

Чтобы справиться с этим масштабируемым способом, я написал django-websocket-redis, модуль Django, который может открывать все эти длинные жизненные соединения Websocket в одном потоке/процессе используя Redis в качестве промежуточной очереди сообщений.

Ответ 3

Вы можете дать звёздные врата bash: http://boothead.github.com/stargate/ и http://pypi.python.org/pypi/stargate/.

Он построен на вершине пирамиды и eventlet (я также предоставил справедливый бит поддержки и тестов websocket для eventlet). Большим преимуществом пирамиды для такого рода вещей является то, что она получила концепцию ресурса, на которую ссылается url, а не только результат вызываемого. Таким образом, вы получаете график постоянных ресурсов, который сопоставляется с вашей структурой URL-адресов, а соединения с веб-серверами просто маршрутизируются и подключаются к этим ресурсам.

Итак, вам нужно всего лишь сделать две вещи:

class YourView(WebSocketView):

    def handler(self, websocket):
        self.request.context.add_listener(websocket)
        while True:
            msg = websocket.wait()
            # Do something with message

Получение сообщений и

resource.send(some_other_message)

Здесь ресурс - это экземпляр stargate.resource.WebSocketAwareContext(as self.request.context) выше, и метод отправки отправляет сообщение всем клиентам, связанным с методом add_listener.

Чтобы опубликовать сообщение всем подключенным клиентам, вы просто вызываете node.send(message)

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

Не стесняйтесь пинговать меня на github, если вам нужна помощь.

Ответ 4

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