我使用一个简单的Flask应用程序和gunicorn的gevent worker来为服务器发送的事件提供服务。
要播放内容,请使用:
response = Response(eventstream(), mimetype="text/event-stream")
从redis中传输事件:
def eventstream():
for message in pubsub.listen():
# ...
yield str(event)
部署:
gunicorn -k gevent -b 127.0.0.1:50008 flaskapplication
但是在使用了一段时间之后,即使没有人连接到服务器发送的事件流,我也会打开50个redis连接。
看来,就像视图没有终止一样,因为gunicorn是非阻塞的而pubsub.listen()是阻塞的。
我该如何解决这个问题?我应该限制gunicorn可能产生的进程数量,还是应该在一些超时后烧掉视图?如果可能,它应该在不活动时停止view / redis连接,而不断开仍连接到SSE流的用户。
答案 0 :(得分:2)
您可以使用gunicorn
运行-t <seconds>
来指定工作人员的超时时间,如果他们静默几秒钟就会杀死他们,通常是30秒。我认为这应该适用于你的问题,但不完全确定。
从我所看到的情况来看,您似乎也可以重写您的工作人员以使用Timeout
中的gevent
。
这可能类似于以下内容:
from gevent import Timeout
def eventstream():
pubsub = redis.pubsub()
try:
with Timeout(30) as timeout:
pubsub.subscribe(channel)
for message in pubsub.listen():
# ...
yield str(event)
except Timeout, t:
if t is not timeout:
raise
else:
pubsub.unsubscribe(channel)
This example有助于了解这应该如何运作。
答案 1 :(得分:0)
使用natdempk解决方案中的Timeout
对象,最优雅的解决方案是发送心跳,以检测死连接:
while True:
pubsub = redis.pubsub()
try:
with Timeout(30) as timeout:
for message in pubsub.listen():
# ...
yield str(event)
timeout.cancel()
timeout.start()
except Timeout, t:
if t is not timeout:
raise
else:
yield ":\n\n" # heartbeat
请注意,您需要再次调用redis.pubsub()
,因为redis连接在异常后丢失,您将收到错误NoneType object has no attribute readline
。