Вопрос по tornado, django, python, socket.io, websocket – Python Socket.IO клиент для отправки широковещательных сообщений на сервер TornadIO2

19

Я создаю веб-приложение в реальном времени.I want to be able to send broadcast messages from the server-side implementation of my python application.

Вот установка:

socketio.js on the client-side TornadIO2 server as Socket.IO server python on the server-side (Django framework)

Я могу успешно отправлять сообщения socket.io от клиента на сервер. Сервер обрабатывает их и может отправить ответ. Далее я опишу, как я это сделал.

Current Setup and Code

Во-первых, нам нужно определить соединение, которое обрабатывает события socket.io:

class BaseConnection(tornadio2.SocketConnection):
    def on_message(self, message):
        pass

    # will be run if client uses socket.emit('connect', username)
    @event
    def connect(self, username):
        # send answer to client which will be handled by socket.on('log', function)
        self.emit('log', 'hello ' + username)

Запуск сервера осуществляется с помощью пользовательского метода управления Django:

class Command(BaseCommand):
    args = ''
    help = 'Starts the TornadIO2 server for handling socket.io connections'

    def handle(self, *args, **kwargs):
        autoreload.main(self.run, args, kwargs)

    def run(self, *args, **kwargs):
        port = settings.SOCKETIO_PORT

        router = tornadio2.TornadioRouter(BaseConnection)

        application = tornado.web.Application(
            router.urls,
            socket_io_port = port
        )

        print 'Starting socket.io server on port %s' % port
        server = SocketServer(application)

Очень хорошо, сервер работает сейчас. Давайте добавим код клиента:

<script type="text/javascript">    
    var sio = io.connect('localhost:9000');

    sio.on('connect', function(data) {
        console.log('connected');
        sio.emit('connect', '{{ user.username }}');
    });

    sio.on('log', function(data) {
        console.log("log: " + data);
    });
</script>

Очевидно, что{{ user.username }} будет заменено именем пользователя, вошедшего в систему в данный момент, в этом примере это имя пользователя & quot; alp & quot ;.

Теперь каждый раз, когда страница обновляется, вывод консоли:

connected
log: hello alp

Поэтому вызов сообщений и отправка ответов работает.But now comes the tricky part.

Problems

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

Итак, вот мои вопросы:

How can i send a broadcast message to all connected clients?

How can i send a broadcast message to multiple connected clients that are subscribed on a specific channel?

How can i send a broadcast message anywhere in my python code (outside of the BaseConnection class)? Would this require some sort of Socket.IO client for python or is this builtin with TornadIO2?

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

Я не работаю с торнадо, но когда я создал приложение с похожим функционалом на Gevent. Gevent не хранит пул подключенных пользователей, и я добавляю все новые входящие подключения в список, и при необходимости отправляю сообщение объектам в этом списке)) Denis
Хорошо, это была бы одна возможность. Но, насколько я знаю, socket.io поддерживает широковещательные сообщения. Может быть, есть способ их использовать? Alp

Ваш Ответ

3   ответа
3

потому что это трудно писать в разделе комментариев. Вы можете просмотреть примеры для tornadoio2 в каталоге примеров, где вы можете найти реализацию чата, и:

class ChatConnection(tornadio2.conn.SocketConnection):
    # Class level variable
    participants = set()

    def on_open(self, info):
        self.send("Welcome from the server.")
        self.participants.add(self)

    def on_message(self, message):
        # Pong message back
        for p in self.participants:
            p.send(message)

Как видите, они реализованы участниками как установлено))

чат - это лишь небольшая часть моего веб-приложения, в реальном времени можно сделать гораздо больше Alp
спасибо за показ, как это работает. но, как отметил Ювал Адам, лучше использовать что-то вроде Redis, так как мое приложение будет довольно большим и должно хорошо масштабироваться. Alp
Это наивная реализация. Что-нибудь более серьезное, чем это, должно использовать правильный паб-саб бэкэнд (см. Мой ответ).
Могу поспорить, что это решение быстрее, чем использование Redis с Tornado. Вы хотите использовать Redis, если вы используете сервер без сохранения состояния, а не Tornado, который выполняется как один процесс.
@Alp Если вы действительно хотите создать простое приложение для чата, вам не нужен какой-либо из db backends.
2

почему бы не взглянуть на gevent-socketio.

Я попробовал это, но у него тоже были проблемы с вещанием. я переключился на sockjs + tornado + redis, и он работает замечательно Alp
16

поэтому у меня есть несколько идей.

Правильный способ сделать то, что вам нужно, это иметь паб-саб-сервер. Есть так много, что вы можете сделать с простымConnectionHandlers. В конце концов, обработка наборов соединений на уровне класса начинает становиться уродливой (не говоря уже о глючности).

В идеале вы хотите использовать что-то вроде Redis с асинхронными привязками к торнадо (см.brukva). Таким образом, вам не придется связываться с регистрацией клиентов на определенных каналах - Redis имеет все это из коробки.

По сути, у вас есть что-то вроде этого:

class ConnectionHandler(SockJSConnection):
    def __init__(self, *args, **kwargs):
        super(ConnectionHandler, self).__init__(*args, **kwargs)
        self.client = brukva.Client()
        self.client.connect()
        self.client.subscribe('some_channel')

    def on_open(self, info):
        self.client.listen(self.on_chan_message)

    def on_message(self, msg):
        # this is a message broadcast from the client
        # handle it as necessary (this implementation ignores them)
        pass

    def on_chan_message(self, msg):
        # this is a message received from redis
        # send it to the client
        self.send(msg.body)

    def on_close(self):
        self.client.unsubscribe('text_stream')
        self.client.disconnect()

Обратите внимание, что я использовалsockjs-торнадо который я нашел гораздо более стабильным, чем socket.io.

В любом случае, если у вас есть такая настройка, отправка сообщений с любого другого клиента (например, Django, в вашем случае) так же проста, как открытие соединения Redis (Redis-ру безопасная ставка) и опубликовать сообщение:

import redis
r = redis.Redis()
r.publish('text_channel', 'oh hai!')

Этот ответ получился довольно длинным, поэтому я приложил дополнительные усилия и сделал из этого пост в блоге:http://blog.y3xz.com/blog/2012/06/08/a-modern-python-stack-for-a-real-time-web-application/

Чувак, ты вообще читаешь вопрос? Этот вопрос: «Есть ли в функции торнадо, который отправляет сообщение всем подключенным клиентам», но не о том, как создать серверную часть для объектов подключения к магазину.
@Yuval Адам: Не могли бы вы объяснить, почему вы нашли sockjs лучше, чем socket.io? Я все еще могу переключиться, поскольку я не привязан к конкретной библиотеке Alp
@ Денис, я думаю, что Ювал Адам ответил именно то, что я хотел знать. Я еще не уверен на 100%, но, пожалуйста, рассмотрите возможность отмены вашего отрицательного голоса. Alp
Конечно. Я также начал с socket.io и фактически обсудил его с MrJoes (сопровождающим tornadio / sockjs-tornado). Он утверждал, что sockjs имеет 100% тестовое покрытие, и что у socket.io, как известно, есть некоторые ошибки протокола. Я обнаружил, что это правда, когда переход на производство sockjs действительно показался более прочным (хотя у нас были некоторые другие проблемы, не связанные с транспортом).
@Alp - для «обычного» В приложениях эта настройка работает как шарм. Приложение, которое я создал, делаетserious обмен сообщениями (десятки сообщений в секунду, не спрашивайте;)) и оказывается, что Chrome действительно не нравится такой материал. Chrome зависнет через 1-5 минут после использования WebSocket.

Похожие вопросы