Обновление браузера не отправляет идентификатор последнего события, полученный как часть ответа, отправленного сервером

Я пытаюсь создать систему уведомлений в реальном времени для пользователей, использующих события, отправленные сервером в python.

Проблема, с которой я сталкиваюсь, заключается в том, что когда я обновляю браузер, это означает, что браузер затем попытается снова нажать URL-адрес EventSource, и согласно документам он должен отправить event.lastEventId как часть заголовков в следующем запросе. Я получаю None каждый раз, когда обновляю страницу.

<!DOCTYPE html>
<html>
    <header><title>SSE test</title></header>
    <body>
        <ul id="list"></ul>
        <script>
        const evtSource = new EventSource("/streams/events?token=abc");
        evtSource.onmessage = function(event) {
            console.log('event', event)
            console.log('event.lastEventId', event.lastEventId)
            const newElement = document.createElement("li");
            const eventList = document.getElementById("list");
            newElement.innerHTML = "message: " + event.data;
            eventList.appendChild(newElement);
        }
        </script>
    </body>
</html>

На стороне сервера

from sse_starlette.sse import EventSourceResponse
from asyncio.queues import Queue
from starlette.requests import Request

@event_router.get(
"/streams/events",
status_code=HTTP_200_OK,
summary='',
description='',
response_description='')
async def event_stream(request: Request):
    return EventSourceResponse(send_events(request))


async def send_events(request: Request):
    try:
        key = request.query_params.get('token')
        last_id = request.headers.get('last-event-id')
        print('last_id ', last_id) # this value is always None

        connection = Queue()
        connections[key] = connection

        while RUNNING:
            next_event = await connection.get()
            print('event', next_event)
            yield dict(data=next_event, id=next_event['id'])
            connection.task_done()
    except asyncio.CancelledError as error:
        pass

Теперь, согласно каждому документу по SSE, когда клиент повторно подключается или обновляет страницу, он отправляет идентификатор последнего события в заголовках. Я пытаюсь прочитать его с помощью request.headers.get('last-event-id'), но это всегда ноль.

Любые указатели на то, как получить последний идентификатор события, будут полезны. Кроме того, как мне убедиться, что я не отправлю те же события даже позже, когда пользователь увидит события, поскольку вся моя логика будет основана на последнем идентификаторе события, полученном сервером, поэтому, если после прочтения его нет события с идентификатором от 1 до 4, как я могу убедиться, что на сервере я не должен отправлять их обратно, даже если идентификатор последнего события равен нулю для пользователя

.

Добавление снимков браузера

1-й рисунок показывает, что события принимаются браузером. например {альфа: азбука, идентификатор: 4}

2-й рисунок показывает, что полученное событие правильно устанавливает lastEventId.

введите здесь описание изображения

введите здесь описание изображения


person Cyclotron3x3    schedule 28.04.2020    source источник


Ответы (1)


Я думаю, что это неправильное понимание. Откуда вы взяли часть, в которой говорится, что когда клиент повторно подключается или обновляет страницу, он отправляет идентификатор последнего события в заголовках.

Насколько я понимаю, последний идентификатор отправляется при повторном подключении. Когда вы обновляете страницу напрямую, это не рассматривается как разорванное соединение и повторное соединение. Вы полностью перезапускаете всю страницу и формируете новое соединение SSE с сервером из браузера. Имейте в виду, если бы ваш SSE имел определенный параметр запроса, например, и это могло быть вызвано чем-то на странице. Вы можете открыть две вкладки с одной и той же страницы, но поместить на страницу разные элементы, что приведет к тому, что этот параметр запроса будет другим в вашем SSE. Это два разных соединения SSE. Одно не влияет на другое, за исключением того, что вы можете достичь максимального предела подключения браузера, если у вас их слишком много. Точно так же, если вы нажмете «Обновить», это похоже на создание новой вкладки. Это совершенно новое соединение.

person BoBoCoding    schedule 28.08.2020
comment
Существуют ли какие-либо рекомендации по использованию sse в веб-приложении для обновлений в реальном времени для зарегистрированных пользователей? Я не хочу использовать сокеты для использования слишком большого количества ресурсов сервера при большой нагрузке. - person Cyclotron3x3; 29.08.2020
comment
Там приличная сумма, если поискать. Если ваша инфраструктура поддерживает HTTP2, на мой взгляд, это, как правило, лучший вариант. Но нет необходимости запускать и запускать SSE. Лучшие практики всегда должны соответствовать вашим конкретным потребностям. - person BoBoCoding; 31.08.2020