現在、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は完全に単純なループなので、これをどのように行うべきかわかりません。Tornadoとbrükvaを使用する場合は、コールバックのみを使用します。
余分な(おそらくトピック外の)質問
私はすでにRedisを使用しているため、次の 2 つの方法のどちらを使用する必要がありますか。
- 「クラシック」Web アプリのように、すべてのコントローラー/ビューを用意し、メッセージングのみにRedisを使用します。
- Web アプリは、タスク キュー (最も単純なPython RQ ) としても使用される、クライアントとRedisの間の単なるレイヤーである必要があります。すべてのリクエストはワーカーに委任する必要があります。
編集
http://goldfirestudios.com/blog/136/Horizontally-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 チャネルch1とch2をサブスクライブし、それらのチャネルから受信したすべてのメッセージを 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つのソースのいずれかから受信したメッセージであるイベントで読み取りを実行したい。