【发布时间】:2018-05-14 22:43:34
【问题描述】:
我正在尝试使用 aiohttp WebSockets 和 aioredis 创建简单的活动用户计数器进行存储。当我在Google Chrome 中添加一个新选项卡时,我的计数器会在所有已打开的选项卡中完美地递增。但是,当我关闭一个选项卡时,其他选项卡没有任何变化。
我认为我应该在整个 async/await 机器中遗漏一些东西,但找不到可能出错的地方。
这是我的应用程序
import asyncio
import aiohttp
from aiohttp import web
import aioredis
class CounterView(web.View):
async def get(self):
request = self.request
app = request.app
ws = web.WebSocketResponse()
app['websockets'].append(ws)
await ws.prepare(request)
count = int(await app['db'].incr('counter'))
for ws in app['websockets']:
await ws.send_json({'msg': {'count': count}})
async for msg in ws:
if msg.type == aiohttp.WSMsgType.TEXT:
await ws.send_str(msg.data)
elif msg.type == aiohttp.WSMsgType.ERROR:
print('ws connection closed with exception %s' %
ws.exception())
app['websockets'].remove(ws)
# Execution stops here (on await app['db'] ...) and never returns
count = int(await app['db'].decr('counter'))
for ws in app['websockets']:
await ws.send_json({'msg': {'count': count}})
return ws
async def init_app(loop):
app = web.Application(loop=loop)
db = await aioredis.create_redis('redis://localhost', loop=loop)
app['db'] = db
app['websockets'] = []
app.add_routes([
web.get('', CounterView),
])
return app
if __name__ == '__main__':
loop = asyncio.get_event_loop()
web.run_app(init_app(loop))
和 index.html 模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
How many people seeing this page now: <span id="counter"></span>
</body>
<script>
window.onload = function () {
const ws = new WebSocket('ws://localhost:8080');
ws.onmessage = function (event) {
const data = JSON.parse(event.data);
let span = document.getElementById('counter');
console.log(data.msg.count);
span.innerHTML = data.msg.count;
}
};
</script>
</html>
我也试过Firefox,那里发生了一些非常奇怪的事情。
打开了两个标签,两个标签都有counter = 2。然后首先重新加载 - 得到1,第二个仍然是2。再次重新加载第一个标签 - 得到2。在此之后,每次重新加载都会给出2。
直到我重新加载第二个选项卡 - 相同的过程(重新加载 - 1 - 重新加载 - 2 发生在那里并在第一个选项卡中重复)
我也尝试应用https://stackoverflow.com/a/48695448/6627564这个答案,但没有任何改变。
调试显示代码最多执行到count = int(await app['db'].decr('counter')),然后跳转到某个地方永远不会返回。
非常感谢任何帮助。据我了解,事件循环应该在这一行之后返回执行。也许协程以某种方式被破坏了,但是我在库中没有找到任何这样做的代码。
我的问题与Python Asyncio Websocket not detecting a disconnect on wifi but does on localhost中描述的不同
首先,我的连接都在本地主机上。
其次,async for msg in ws循环后的代码实际开始执行,调试显示实际调用了ws.close()方法。但是在下一个await 上有一个上下文切换,并且执行不会继续进行。
我也尝试使用ws = web.WebSocketResponse(heartbeat=1.0) 来激活乒乓球,但我在开发工具中看不到任何消息。我在await ws.prepare(request) 之后添加了单个await ws.ping(),不幸的是,开发工具中没有出现任何消息。这里肯定有问题...
【问题讨论】:
标签: python websocket redis python-asyncio aiohttp