以下使用node.js给出了答案。
How to close a "Server-Sent Events"-connection on the server?
但是,如何在python Flask中做同样的事情?
答案 0 :(得分:3)
嗯,这取决于你应用的架构。
让我向您展示一个示例(请参阅https://github.com/jkbr/chat/blob/master/app.py上的此代码):
def event_stream():
pubsub = red.pubsub()
pubsub.subscribe('chat')
for message in pubsub.listen():
print message
yield 'data: %s\n\n' % message['data']
@app.route('/stream')
def stream():
return flask.Response(event_stream(),
mimetype="text/event-stream")
Flask稳定地向Redis(锁定操作)发出一条新消息,但是当Flask看到流终止(StopIteration
时,如果你不熟悉Python),它会返回。
def event_stream():
pubsub = red.pubsub()
pubsub.subscribe('chat')
for message in pubsub.listen():
if i_should_close_the_connection:
break
yield 'data: %s\n\n' % message['data']
@app.route('/stream')
def stream():
return flask.Response(event_stream(),
mimetype="text/event-stream")
答案 1 :(得分:2)
请务必注意,您也需要在客户端上关闭连接,否则将在retry
超时后尝试重新打开连接。
这让我感到困惑,直到我在这里看到这篇文章:https://stackoverflow.com/a/38235218/5180047
通过链接:
这里的问题是服务器意外关闭了连接,而不是在保持打开状态的情况下完成了工作。发生这种情况时,客户端会重新发送打开连接并开始流式发送服务器已发送事件的请求。然后服务器一次又一次关闭连接,导致无限循环。
看到此消息后。我的首选解决方案是通过产生预期的数据消息来关闭服务器的连接。
在客户端(浏览器)上
var sse = new EventSource('/sse');
sse.addEventListener('message', function(e) {
console.log(e);
var data = e.data;
if (!data) {
console.log('no data in event');
return;
}
if (data === 'finished') {
console.log('closing connection')
sse.close()
}
// my work here based on message
});
我的Flask服务器
from flask import Response
@app_api.route("/sse")
def stream():
def trackerStream():
finished = check_if_finished()
while not finished:
sleep(1) # poll timeout
if should_send_event():
yield f"data: {mydata()}\n\n"
else:
yield "data: nodata\n\n"
finished = check_if_finished()
yield "data: finished\n\n"
return Response(trackerStream(), mimetype="text/event-stream")
注意:确保始终以一定的时间间隔将事件从服务器发送到客户端。如果用户关闭浏览器,则flask在尝试写入套接字时会出错,并会为您关闭流。如果您不按一定的时间间隔写客户机,即使您只是写data: nodata\n\n
,服务器也可能陷入循环。
答案 2 :(得分:0)
我遇到了同样的问题,终于找到了以下解决方法here。
import time
from flask import Flask, Response, stream_with_context
app = Flask(__name__)
@app.route('/stream')
def stream():
def gen():
try:
i = 0
while True:
data = 'this is line {}'.format(i)
print(data)
yield data + '<br>'
i += 1
time.sleep(1)
except GeneratorExit:
print('closed')
return Response(stream_with_context(gen()))
您可以通过向Redis订阅侦听器来启动该方法。您可以等待Redis的time.sleep(1)
方法返回vlaue来代替listen()
。您可以取消订阅Redis,而不是print('closed')
。剩下的唯一问题是,仅在将yield值发送到客户端时才引发GeneratorExit
异常。因此,如果Redis的listen()
永无止境,那么您将永远不会发现连接已断开。