首页 > 解决方案 > 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() 的每条消息的消息,如此 处所示

我无法弄清楚为什么事件源在收到每条消息后都会出错,或者如何防止这种情况发生。

标签: pythonhtmlflaskserver-sent-events

解决方案


这个问题的答案不是代码本身的问题。

这最终成为机器上使用的防病毒安全性问题。使用 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 软件最终导致我的问题的原因。


推荐阅读