python - JavaScript EventSource 在收到每条消息后触发 onerror
问题描述
我有一个使用 Python Flask 的 Web 应用程序,我正在尝试使用服务器发送事件 (SSE) 将消息推送到网页,而无需从客户端轮询或请求该数据。我正在使用 Redis 监听新数据,然后将其发送到网页。为了开始并确保我可以正确使用 SSE,我使用了类似于这样的示例的模板(如何在 Flask 框架中实现服务器推送?)。
我遇到的问题是每次客户端收到消息时,都会调用 EventSource onmessage() 方法并正确传递消息,但随后会立即触发 .onerror() 方法,导致客户端尝试重新连接. 这会导致“/listen”端点被一遍又一遍地调用,从而导致创建许多冗余并订阅相同频道的 redis pubsub 对象。
运行flask app的python代码如下
import flask
from flask_bootstrap import Bootstrap
from redis import Redis
from flask_wtf import FlaskForm
app = flask.Flask(__name__)
bootstrap = Bootstrap(app)
red = Redis(host='localhost', port=6379, db=0)
@app.route('/listen')
def listen():
pubsub = red.pubsub()
pubsub.subscribe('chat')
def stream():
for message in pubsub.listen():
if message['type'] == 'message':
msg = f"data: {message['data'].decode('utf-8')}\n\n"
yield msg
for msg in stream():
return Response(msg, mimetype='text/event-stream')
@app.route('/sse_page', methods=['GET', 'POST'])
def sse_page():
form = FlaskForm()
return render_template('sse_page.html', title='Server Push Testing', form=form)
if __name__ == "__main__":
app.run(port=8000, threaded=True, debug=True)
我尝试打开 EventSource 并侦听事件流的 sse_page.html 的相应部分是
<body>
<div id="target_div">Watch this space...</div>
</body>
<script>
var source = new EventSource("/listen");
source.onmessage = function (event) {
console.log('data: ', event)
$("#target_div").text(event.data)
};
source.onerror = function (event) {
console.log('error ', event)
};
source.onopen = function (event) {
console.log('open', event)
};
</script>
使用 redis-cli 发送像 这里看到的那样的消息(并在下面转录)
127.0.0.1:6379> publish chat a
(integer) 1
127.0.0.1:6379> publish chat b
(integer) 2
导致控制台记录来自 Eventsource.onopen()、Eventsource.onmessage() 和 Eventsource.onerror() 的每条消息的消息,如此 处所示。
我无法弄清楚为什么事件源在收到每条消息后都会出错,或者如何防止这种情况发生。
解决方案
这个问题的答案不是代码本身的问题。
这最终成为机器上使用的防病毒安全性问题。使用 Sophos AV Endpoint 会导致每个 SSE 都被视为下载,因此在“下载完成”之前,任何文本数据都无法流式传输。
这是(显然)一个已知问题(请参阅链接https://community.sophos.com/on-premise-endpoint/f/sophos-endpoint-software/74878/server-sent-events-blocked-by-download-scanner ) 并且有几种方法可以处理它。您可以通过 Sophos 禁用 Web 扫描(如果您没有管理员权限,这将不起作用)或通过 HTTPS 安全地运行烧瓶应用程序(https://blog.miguelgrinberg.com/post/running-your-flask-application- over-https有一个很棒的教程)。
也应该归功于这篇文章(JavaScript EventSource SSE 没有在浏览器中触发),这就是我能够发现 AV 软件最终导致我的问题的原因。
推荐阅读
- laravel - Laravel 立即保存具有关系的模型
- mysql - 如何组合三个表中的列并根据月份将它们显示在一行中?
- django - 访问 'localhost:8080/signin' 给我错误代码:SSL_ERROR_RX_RECORD_TOO_LONG
- ios - 如何对 dispatch_group_leave 崩溃进行故障排除
- python - python 太快了,gnuplot 无法完成它的工作
- r - 如何使用 R 中的值命名新目录(dir.ceate)
- python - 在+ve斜率中找到大于阈值的索引的最佳方法
- wpf - 如何在全屏窗口的画布内定位 WPF 控件?
- python - Python Programmatic access to VBA in Excel
- web-scraping - 如何使用 Google API 为给定查询获取 300 多个 Google 结果?