18

現在、aiohttpで遊んで、websocket 接続を使用したモバイル アプリのサーバー アプリケーションとしてどのように機能するかを確認しています。

簡単な「Hello world」の例を次に示します (要旨はこちら):

import asyncio
import aiohttp
from aiohttp import web


class WebsocketEchoHandler:

    @asyncio.coroutine
    def __call__(self, request):
        ws = web.WebSocketResponse()
        ws.start(request)

        print('Connection opened')
        try:
            while True:
                msg = yield from ws.receive()
                ws.send_str(msg.data + '/answer')
        except:
            pass
        finally:
            print('Connection closed')
        return ws


if __name__ == "__main__":
    app = aiohttp.web.Application()
    app.router.add_route('GET', '/ws', WebsocketEchoHandler())

    loop = asyncio.get_event_loop()
    handler = app.make_handler()

    f = loop.create_server(
        handler,
        '127.0.0.1',
        8080,
    )

    srv = loop.run_until_complete(f)
    print("Server started at {sock[0]}:{sock[1]}".format(
        sock=srv.sockets[0].getsockname()
    ))
    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass
    finally:
        loop.run_until_complete(handler.finish_connections(1.0))
        srv.close()
        loop.run_until_complete(srv.wait_closed())
        loop.run_until_complete(app.finish())
    loop.close()

問題

ここで、以下に説明する構造を使用したいと思います (ノード サーバー = python aiohttp)。より具体的には、asyncio-redisでRedis Pub/Subメカニズムを使用して、websocket 接続とWebsocketEchoHandlerの Redis の両方を読み書きします。

WebsocketEchoHandlerは完全に単純なループなので、これをどのように行うべきかわかりません。Tornadobrükvaを使用する場合は、コールバックのみを使用します。

http://goldfirestudios.com/blog/136/Horizo​​ntally-Scaling-Node.js-and-WebSockets-with-Redis

余分な(おそらくトピック外の)質問

私はすでにRedisを使用しているため、次の 2 つの方法のどちらを使用する必要がありますか。

  1. 「クラシック」Web アプリのように、すべてのコントローラー/ビューを用意し、メッセージングのみにRedisを使用します。
  2. Web アプリは、タスク キュー (最も単純なPython RQ ) としても使用される、クライアントとRedisの間の単なるレイヤーである必要があります。すべてのリクエストはワーカーに委任する必要があります。

編集

http://goldfirestudios.com/blog/136/Horizo​​ntally-Scaling-Node.js-and-WebSockets-with-Redisからの画像

編集2

明確にする必要があるようです。

  • Websocket のみのハンドラーを上に示します
  • Redis Pub/Sub ハンドラは次のようになります。

    class WebsocketEchoHandler:
    
        @asyncio.coroutine
        def __call__(self, request):
            ws = web.WebSocketResponse()
            ws.start(request)
    
            connection = yield from asyncio_redis.Connection.create(host='127.0.0.1', port=6379)
            subscriber = yield from connection.start_subscribe()
            yield from subscriber.subscribe(['ch1', 'ch2'])
    
            print('Connection opened')
            try:
                while True:
                    msg = yield from subscriber.next_published()
                    ws.send_str(msg.value + '/answer')
            except:
                pass
            finally:
                print('Connection closed')
            return ws
    

    このハンドラーは、Redis チャネルch1ch2をサブスクライブし、それらのチャネルから受信したすべてのメッセージを websocket に送信します。

  • このハンドラーが必要です:

    class WebsocketEchoHandler:
    
        @asyncio.coroutine
        def __call__(self, request):
            ws = web.WebSocketResponse()
            ws.start(request)
    
            connection = yield from asyncio_redis.Connection.create(host='127.0.0.1', port=6379)
            subscriber = yield from connection.start_subscribe()
            yield from subscriber.subscribe(['ch1', 'ch2'])
    
            print('Connection opened')
            try:
                while True:
                    # If message recived from redis OR from websocket
                    msg_ws = yield from ws.receive()
                    msg_redis = yield from subscriber.next_published()
                    if msg_ws:
                        # push to redis / do something else
                        self.on_msg_from_ws(msg_ws)
                    if msg_redis:
                        self.on_msg_from_redis(msg_redis)
            except:
                pass
            finally:
                print('Connection closed')
            return ws
    

    ただし、次のコードは常に順次呼び出されるため、websocket からの読み取りは Redis からの読み取りをブロックします。

    msg_ws = yield from ws.receive()
    msg_redis = yield from subscriber.next_published()
    

イベントが2つのソースのいずれかから受信したメッセージであるイベントで読み取りを実行したい。

4

2 に答える 2