也许这是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();
}
}
有什么不对吗?
任何有代码的建议或解决方案都会受到赞赏!
提前致谢!
答案 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!"
的无限流,您将知道完成后在哪里可以运行清除代码。如果您的生成器阻塞了线程,则需要以某种方式(可能是推动虚拟项目)将其解除阻塞,以便可以关闭生成器。