Flask+gevent - SSE 在使用 nginx+uwsgi 时超时

8

我正在开发一个基于Flask、gevent和Redis的Web应用程序,使用了Server Sent Events。

我在StackOverflow上阅读了很多问题,并且在Google上进行了广泛的搜索,但是没有找到适合我的答案,所以现在请求社区的帮助。

问题出在生产环境nginx+uwsgi上:浏览器只能持续接收更新并刷新页面约30秒,之后连接超时,浏览器不再接收任何更新,直到手动重新加载页面。

由于在本地主机上,标准的flask开发服务器可以完美运行(在30分钟闲置后连接仍然保持活跃),所以我相信问题出在uwsgi/nginx配置上。我尝试了我能想到的所有nginx/uwsgi设置,但是都无法解决这个问题。

有人知道怎么解决吗?

以下是相关代码和配置文件。

生产环境nginx设置:

location / {
include uwsgi_params;
uwsgi_pass unix:/tmp/myapp.sock;
uwsgi_param UWSGI_PYHOME /srv/www/myapp/venv;
uwsgi_param UWSGI_CHDIR /srv/www/myapp;
uwsgi_param UWSGI_MODULE run;
uwsgi_param UWSGI_CALLABLE app;
uwsgi_buffering off;
proxy_set_header Connection '';
proxy_http_version 1.1;
chunked_transfer_encoding off;
proxy_cache off;
}

uwsgi生产环境设置

[uwsgi]
base = /srv/www/myapp
app = run
home = %(base)/venv
pythonpath = %(base)
socket = /tmp/%n.sock
gevent = 100
module = %(app)
callable = app
logto = /srv/www/myapp-logs/uwsgi_%n.log

这是模板执行订阅频道的JavaScript代码(目前,模板在服务器推送数据时仅刷新整个页面)。
<script type="text/javascript">

var eventOutputContainer = document.getElementById("event");
var evtSrc = new EventSource("/movers/monitor");

evtSrc.onmessage = function(e) {
    console.log(e.data);
    location.reload();
    //eventOutputContainer.innerHTML = e.data;
};
</script>

这是我用来返回流数据的代码。
from myapp import redislist
from flask import Response, Blueprint, stream_with_context

movers = Blueprint('movers', __name__, url_prefix='/movers')
r = redislist['r']

@movers.route("/monitor")
def stream_movers():
    def gen():
        pubsub = r.pubsub()
        pubsub.subscribe('movers-real-time')
        for event in pubsub.listen():
            if event['type'] == 'message':
                yield 'retry: 10000\n\ndata: %s\n\n' % event['data']

    return Response(stream_with_context(gen()), direct_passthrough=True, mimetype="text/event-stream")

最后,应用程序像这样执行(在本地主机上DEBUG为True)。
from myapp import app
from gevent.wsgi import WSGIServer

if __name__ == '__main__':
    DEBUG = True if app.config['DEBUG'] else False
    if DEBUG:
        app.run(debug=DEBUG, threaded=True)
        app.debug = True
        server = WSGIServer(("", 5000), app)
        server.serve_forever()
    else:
        server = WSGIServer("", app)
        server.serve_forever()
1个回答

4
经过长时间的研究nginx日志文件和Firefox js控制台,结果发现问题所示的配置完全没有问题。
问题在于页面重新加载,此操作会终止并重新初始化连接,因此重试命令不起作用。
删除该指令后,即使经过长时间的闲置,SSE更新也能正常工作。
现在的问题是为什么这在更简单的开发环境中有效 :-)
编辑
事实上,在几天之后,连接仍然超时。我进行了一些时间测量,并发现超时间隔在某些30秒到几分钟的不活动时间之间是可变的。
我的结论是,上面的堆栈很好,而亚马逊EC2连接在一些可变的不活动时间之后会过期,因为我仍在使用微型实例。
最终修复如下JS片段:
    evtSrc.onerror = function(e) {
        location.reload();
    }

当连接中断时(无论原因是什么),页面会重新加载。当服务器发送事件频繁时,不应该发生重新加载。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接