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

时间:2013-08-22 14:15:28

标签: python flask server-sent-events

也许这是Flask中的问题,无法在服务器端处理断开连接事件。

在Response类中,有一个名为“call_on_close”的方法,我们可以在其中添加一个没有参数的函数,例如: on_close(),它将在响应对象的close方法被调用时被触发,但是当我在Javascript中从客户端调用EventSource.close()时,这不会发生。

服务器端的

代码:

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();
  }
}

有什么不对吗?

任何有代码的建议或解决方案都会受到赞赏!

提前致谢!

3 个答案:

答案 0 :(得分:2)

我遇到过与Rails Live Controllers类似的问题。问题是,在尝试将事件发送到客户端之前,框架似乎没有检测到连接是否已关闭。

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

线程方法的替代方法是使用超时(再次,比如60秒)包装Redis pubsub块。然后将heartbeat事件发送到客户端 - 然后是另一个pubsub调用。这种方法的缺点是,您可能会在未订阅时错过任何活动。

这里的线程方法还有很多: Redis + ActionController::Live threads not dying

答案 1 :(得分:2)

要扩展@Lonami的答案,使用返回数据的非阻塞函数时,必须使用yield

def stream():
    try:
        pubsub = red.pubsub()
        pubsub.subscribe('chat')
        #for message in pubsub.listen():#This doesn't work because it's blocking
        while True:
            message = pubsub.get_message()

            if not message:
                # The yield is necessary for this to work!
                # In my case I always send JSON encoded data
                # An empty response might work, too.
                yield "data: {}\n\n"
                sleep(0.1)
                continue

            # If the nonblocking get_message() returned something, proceed normally
            yield 'data: %s\n\n' % message["data"]
    finally:
        print("CLOSED!")
        # Your closing logic here (e.g. marking the user as offline in your database)


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

答案 2 :(得分:0)

生成器收到一个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!"的无限流,您将知道完成后在哪里可以运行清除代码。如果您的生成器阻塞了线程,则需要以某种方式(可能是推动虚拟项目)将其解除阻塞,以便可以关闭生成器。