20

リアルタイムのWebアプリケーションを構築しています。Pythonアプリケーションのサーバー側の実装からブロードキャストメッセージを送信できるようにしたい。

設定は次のとおりです。

  • クライアント側のsocketio.js
  • Socket.IOサーバーとしてのTornadIO2サーバー
  • サーバー側のPython ( Djangoフレームワーク)

クライアントからサーバーにsocket.ioメッセージを正常に送信できます。サーバーはこれらを処理し、応答を送信できます。以下では、私がそれをどのように行ったかについて説明します。

現在の設定とコード

まず、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 }}、現在ログインしているユーザーのユーザー名に置き換えられます。この例では、ユーザー名は「alp」です。

これで、ページが更新されるたびに、コンソールの出力は次のようになります。

connected
log: hello alp

したがって、メッセージの呼び出しと応答の送信は機能します。しかし、ここで注意が必要な部分があります。

問題

応答「helloalp」は、socket.ioメッセージの呼び出し元にのみ送信されます。接続されているすべてのクライアントにメッセージをブロードキャストして、新しいユーザーがパーティーに参加した場合にリアルタイムで通知できるようにします(チャットアプリケーションなど)。

だから、ここに私の質問があります:

  1. 接続されているすべてのクライアントにブロードキャストメッセージを送信するにはどうすればよいですか?

  2. 特定のチャネルでサブスクライブされている複数の接続されたクライアントにブロードキャストメッセージを送信するにはどうすればよいですか?

  3. BaseConnectionPythonコードのどこにでも(クラス外で)ブロードキャストメッセージを送信するにはどうすればよいですか?これにはPython用のSocket.IOクライアントが必要ですか、それともTornadIO2が組み込まれていますか?

これらのブロードキャストはすべて信頼できる方法で行われる必要があるため、WebSocketが最良の選択だと思います。しかし、私はすべての良い解決策を受け入れています。

4

3 に答える 3

16

私は最近、同様のセットアップで非常に類似したアプリケーションを作成したので、いくつかの洞察があります。

必要なことを行う適切な方法は、pub-subバックエンドを用意することです。ConnectionHandler単純なsでできることはたくさんあります。最終的に、クラスレベルの接続セットの処理は醜くなり始めます(バグは言うまでもありません)。

理想的には、竜巻への非同期バインディングを備えた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()

socket.ioよりもはるかに安定していることがわかったsockjs-tornadoを使用したことに注意してください。

とにかく、この種の設定ができたら、他のクライアント(この場合はDjangoなど)からメッセージを送信するのは、Redis接続を開いて(redis-pyが安全です)、メッセージを公開するのと同じくらい簡単です。

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 /

于 2012-06-08T14:23:06.337 に答える
3

コメント欄に書くのは難しいので、ここに書きます。チャットの実装を見つけることができるexamplesディレクトリで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)

あなたが見ることができるように、彼らはセットとして参加者を実装しました))

于 2012-06-08T14:22:08.863 に答える
2

すでにdjangoを使用している場合は、gevent-socketioをご覧ください。

于 2012-06-09T16:36:28.107 に答える