Python Flask, как определить, что клиент SSE отключается от интерфейса Javascript

Возможно, это проблема в Flask, нет способа обработать событие разъединения на стороне сервера.

В классе Response существует метод с именем "call_on_close", где мы можем добавить функцию без аргумента, например. on_close(), он будет запущен при вызове метода закрытия объекта ответа, но этого не происходит, когда я вызываю EventSource.close() со стороны клиента в Javascript.

код на стороне сервера:

from flask import Response
r = Response(stream(), ...)
r.call_on_close(on_close)
return r 

def on_close():
  print "response is closed!"

def stream():
  ...  # subscribe to redis
  for message in pubsub.listen():
    ....
    yield 'data: %s\n\n' % message

на стороне клиента: добавьте обработчик выгрузки на страницу с SSE

$(window).unload(
  function() {
    sse.close();
  }
}

Что-то не так?

Приветствуются любые предложения или решения с кодом!

Спасибо заранее!

Ответ 1

У меня была аналогичная проблема с Rails Live Controllers. Проблема в том, что структура не обнаруживает, что соединение закрыто, пока оно не попытается отправить событие клиенту.

Один из подходов заключается в отправке клиенту периодических "сердечных" событий. В настоящее время я успешно использую это в проекте Rails с интервалом в 60 секунд. У меня есть отдельный поток, который "испускает" эти пульса в Redis, на который мой контроллер подписался.

Альтернативой поточному подходу является обертывание блока pubsub Redis таймаутом (опять же, скажем, 60 секунд). А затем отправьте событие heartbeat клиенту, а затем еще один вызов pubsub. Недостатком этого подхода является то, что вы можете пропустить мероприятие, пока вы не подписаны.

Здесь больше по подходу к потоку: Redis + ActionController:: Живые потоки не умирают

Ответ 2

Генератор получает исключение GeneratorExit, и, когда вы знаете, он выйдет. Например:

def stream():
    try:
        i = 0
        while True:
            yield 'Hello {}!'.format(i)
            i += 1
            time.sleep(1)
    finally:
        # Called after a GeneratorExit, cleanup here
        i = 0


@app.route('/messages')
def messages():
    return Response(stream(), content_type='text/event-stream')

Даст бесконечный поток "Hello!" , и вы будете знать, когда это будет сделано, где вы можете запустить код очистки. Если ваш генератор блокирует поток, его нужно каким-то образом разблокировать (возможно, нажав на пустышку), чтобы генератор мог быть закрыт.