【发布时间】:2019-06-07 03:06:37
【问题描述】:
我有网络应用程序。该应用程序具有将一些对象数据推送到redis 频道的端点。
另一个端点处理websocket 连接,从通道中获取数据并通过ws 发送到客户端。
当我通过 ws 连接时,消息只获取第一个连接的客户端。
如何使用多个客户端从redis 频道读取消息而不创建新订阅?
Websocket 处理程序。
我在这里订阅频道,将其保存到应用程序(init_tram_channel)。然后运行我收听频道并发送消息的工作(run_tram_listening)。
@routes.get('/tram-state-ws/{tram_id}')
async def tram_ws(request: web.Request):
ws = web.WebSocketResponse()
await ws.prepare(request)
tram_id = int(request.match_info['tram_id'])
channel_name = f'tram_{tram_id}'
await init_tram_channel(channel_name, request.app)
tram_job = await run_tram_listening(
request=request,
ws=ws,
channel=request.app['tram_producers'][channel_name]
)
request.app['websockets'].add(ws)
try:
async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT:
if msg.data == 'close':
await ws.close()
break
if msg.type == aiohttp.WSMsgType.ERROR:
logging.error(f'ws connection was closed with exception {ws.exception()}')
else:
await asyncio.sleep(0.005)
except asyncio.CancelledError:
pass
finally:
await tram_job.close()
request.app['websockets'].discard(ws)
return ws
订阅和保存频道。
每个频道都与唯一的对象相关,为了不创建许多与同一对象相关的频道,我只保存一个到应用程序。
app['tram_producers'] 是字典。
async def init_tram_channel(
channel_name: str,
app: web.Application
):
if channel_name not in app['tram_producers']:
channel, = await app['redis'].subscribe(channel_name)
app['tram_producers'][channel_name] = channel
运行 coro 进行频道监听。 我通过 aiojobs 运行它:
async def run_tram_listening(
request: web.Request,
ws: web.WebSocketResponse,
channel: Channel
):
"""
:return: aiojobs._job.Job object
"""
listen_redis_job = await spawn(
request,
_read_tram_subscription(
ws,
channel
)
)
return listen_redis_job
Coro 我在哪里收听和发送消息:
async def _read_tram_subscription(
ws: web.WebSocketResponse,
channel: Channel
):
try:
async for msg in channel.iter():
tram_data = msg.decode()
await ws.send_json(tram_data)
except asyncio.CancelledError:
pass
except Exception as e:
logging.error(msg=e, exc_info=e)
【问题讨论】:
标签: python redis python-asyncio aiohttp