我正在编写一个基于Flask,gevent和Redis的webapp,它使用Server Sent Events.
我在StackOverflow上经历了几个问题,并在google上进行了大量搜索,但没有找到适合我的合适答案,所以在这里我要求社区帮助.
问题在于生产堆栈,Nginx uwsgi:浏览器定期接收更新(并按预期刷新)大约30秒.之后,连接超时,浏览器不再接收任何更新,直到手动重新加载页面.
由于整个东西在localhost上完美运行,使用标准的烧瓶开发服务器(闲置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(暂时,模板只是在服务器推送一些数据时刷新整个页面)
这是我用来返回流数据的代码
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")
最后app就像这样执行(在localhost上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()
最佳答案
在Nginx日志文件和firefox js控制台上长时间工作之后,事实证明问题中显示的配置完全没问题.
问题是页面重新加载,此操作会终止并重新初始化连接,因此重试命令没有任何效果.
删除该指令后,即使长时间不活动,SSE更新也会像魅力一样发挥作用.
现在问题是为什么这适用于更简单的开发环境堆栈:-)
编辑
事实上,再过几天,连接仍然超时.我做了一些时间测量,发现超时间隔在30秒到几分钟不活动之间变化.
我的结论是上面的堆栈很好,而亚马逊EC2连接在一些变量不活动时间之后到期,因为我仍在使用微实例.
最后的修复是以下JS片段:
evtSrc.onerror = function(e) {
location.reload();
}
页面重新加载连接断开时(无论什么原因).当服务器发送事件频繁时,预计不会发生重新加载.