如何在Flask框架中实现服务器推送?

lit*_*yes 49 python juggernaut redis flask

我正在尝试使用Flask micro-web框架上的服务器推送功能构建一个小型站点,但我不知道是否有一个框架可以直接使用.

我使用了Juggernaut,但它似乎没有在当前版本中使用redis-py,并且Juggernaut最近被弃用了.

有没有人对我的案子有任何建议?

Pet*_*ann 92

看看Server-Sent Events.Server-Sent Events是一个浏览器API,可让您继续打开服务器的套接字,订阅更新流.欲了解更多信息,请阅读Alex MacCaw(Juggernaut的作者)发布关于他为什么杀死主宰的帖子以及为什么更简单的服务器发送事件在manny案例中比Websockets更好的工具.

协议非常简单.只需将mimetype添加text/event-stream到您的回复中即可.浏览器将保持连接打开并监听更新.从服务器发送的事件是以data:换行符和后续换行符开头的一行文本.

data: this is a simple message
<blank line>
Run Code Online (Sandbox Code Playgroud)

如果要交换结构化数据,只需将数据转储为json并通过线路发送json.

一个优点是您可以在Flask中使用SSE而无需额外的服务器.github上有一个简单的聊天应用程序示例,它使用redis作为pub/sub后端.

def event_stream():
    pubsub = red.pubsub()
    pubsub.subscribe('chat')
    for message in pubsub.listen():
        print message
        yield 'data: %s\n\n' % message['data']


@app.route('/post', methods=['POST'])
def post():
    message = flask.request.form['message']
    user = flask.session.get('user', 'anonymous')
    now = datetime.datetime.now().replace(microsecond=0).time()
    red.publish('chat', u'[%s] %s: %s' % (now.isoformat(), user, message))


@app.route('/stream')
def stream():
    return flask.Response(event_stream(),
                          mimetype="text/event-stream")
Run Code Online (Sandbox Code Playgroud)

您不需要使用gunicron来运行示例应用程序.只需确保在运行应用程序时使用线程,否则SSE连接将阻止您的开发服务器:

if __name__ == '__main__':
    app.debug = True
    app.run(threaded=True)
Run Code Online (Sandbox Code Playgroud)

在客户端,您只需要一个Javascript处理函数,当从服务器推送新消息时将调用该函数.

var source = new EventSource('/stream');
source.onmessage = function (event) {
     alert(event.data);
};
Run Code Online (Sandbox Code Playgroud)

最近的Firefox,Chrome和Safari浏览器支持 Server-Sent Events .Internet Explorer尚不支持Server-Sent Events,但预计会在版本10中支持它们.有两种推荐的Polyfill支持旧版浏览器

  • 你怎么能确定Flask关闭连接?如果我重新加载页面很多,我得到一堆陈旧的连接,当我ctrl-C烧瓶应用程序它仍然提供请求,因为有连接打开: - / (5认同)
  • 这将在使用gevent + monkeypatch时进行扩展 (2认同)

fea*_*ool 14

Redis过于强大:使用服务器端事件

聚会晚了(与往常一样),但是使用Redis的恕我直言可能是过分的。

只要您使用的是Python + Flask,就可以考虑使用Panisuan Joe Chasinga的这篇出色文章中描述的生成器函数。其要点是:

在您的客户index.html中

var targetContainer = document.getElementById("target_div");
var eventSource = new EventSource("/stream")
  eventSource.onmessage = function(e) {
  targetContainer.innerHTML = e.data;
};
...
<div id="target_div">Watch this space...</div>
Run Code Online (Sandbox Code Playgroud)

在Flask服务器中:

def get_message():
    '''this could be any function that blocks until data is ready'''
    time.sleep(1.0)
    s = time.ctime(time.time())
    return s

@app.route('/')
def root():
    return render_template('index.html')

@app.route('/stream')
def stream():
    def eventStream():
        while True:
            # wait for source data to be available, then push it
            yield 'data: {}\n\n'.format(get_message())
    return Response(eventStream(), mimetype="text/event-stream")
Run Code Online (Sandbox Code Playgroud)


sin*_*boy 13

作为@ peter-hoffmann回答的后续内容,我编写了一个专门用于处理服务器发送事件的Flask扩展.它被称为Flask-SSE,它可以在PyPI上使用.要安装它,请运行:

$ pip install flask-sse
Run Code Online (Sandbox Code Playgroud)

你可以像这样使用它:

from flask import Flask
from flask_sse import sse

app = Flask(__name__)
app.config["REDIS_URL"] = "redis://localhost"
app.register_blueprint(sse, url_prefix='/stream')

@app.route('/send')
def send_message():
    sse.publish({"message": "Hello!"}, type='greeting')
    return "Message sent!"
Run Code Online (Sandbox Code Playgroud)

并且要从Javascript连接到事件流,它的工作方式如下:

var source = new EventSource("{{ url_for('sse.stream') }}");
source.addEventListener('greeting', function(event) {
    var data = JSON.parse(event.data);
    // do what you want with this data
}, false);
Run Code Online (Sandbox Code Playgroud)

ReadTheDocs提供了文档.请注意,您需要一个正在运行的Redis服务器来处理pub/sub.