Я пытаюсь создать простой счетчик активных пользователей, используя aiohttp
WebSockets и aioredis
для хранения. Когда я добавляю новую вкладку в Google Chrome
, мой счетчик отлично увеличивается во всех уже открытых вкладках. Однако когда я закрываю вкладку, в других вкладках ничего не меняется.
Я думаю, что я должен что-то упустить во всей асинхронной / ожидающей машине, но не могу найти, что может быть не так.
Вот мое приложение
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 не обнаруживает разрыв соединения по Wi-Fi, но обнаруживает на локальном хосте
Прежде всего, мои соединения на всем локальном хосте.
Во-вторых, код после цикла async for msg in ws
фактически начинает выполняться, и отладка показывает, что метод ws.close()
действительно вызывается. НО в следующем await
происходит переключение контекста, и выполнение не идет дальше.
Я также пытался использовать ws = web.WebSocketResponse(heartbeat=1.0)
для активации пинг-понга, но я не вижу никаких сообщений в Dev Tools. Я добавил сингл await ws.ping()
после await ws.prepare(request)
и, к сожалению, в Dev Tools сообщения не появлялись. Здесь что-то определенно идет не так ...