Python Flask,如何从前端Javascript检测SSE客户端断开连接

bwl*_*lee 13 python flask server-sent-events

也许这是Flask的一个问题,没有办法在服务器端处理断开连接事件.

在Response类中,有一个名为"call_on_close"的方法,我们可以在其中添加一个没有参数的函数,例如on_close(),它会在响应对象的close方法被调用时触发,但是当我调用EventSource时这不会发生.在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
Run Code Online (Sandbox Code Playgroud)

在客户端:使用SSE向页面添加卸载处理程序

$(window).unload(
  function() {
    sse.close();
  }
}
Run Code Online (Sandbox Code Playgroud)

什么都错了?

任何建议或解决方案与代码表示赞赏!

提前致谢!

Lon*_*ami 6

生成器收到一个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')
Run Code Online (Sandbox Code Playgroud)

将产生无限流"Hello!",您将知道何时完成,您可以在哪里运行清理代码。如果您的生成器阻塞了线程,则需要以某种方式解除阻塞(可能是推送一个虚拟项目),以便可以关闭生成器。


小智 2

我在使用 Rails Live Controllers 时也遇到过类似的问题。问题在于框架似乎没有检测到连接已关闭,直到它尝试向客户端发送事件。

一种方法是向客户端发送周期性的“心跳”事件。我目前在我的 Rails 项目中成功使用了它,间隔为 60 秒。我有一个单独的线程,将这些心跳“发送”到 Redis 中,我的控制器已订阅该线程。

线程方法的另一种替代方法是使用超时(同样为 60 秒)来包装 Redis pubsub 块。然后将心跳事件发送到客户端 - 接下来是另一个 pubsub 调用。这种方法的缺点是,如果您没有订阅,您可能会错过某个活动。

这里有更多关于线程方法的信息: Redis + ActionController::Livethreads not dead