web-dev-qa-db-fra.com

Comment implémenter le serveur Push in Flask framework?

J'essaie de construire un petit site avec la fonctionnalité Push du serveur sur Flask framework micro-web, mais je ne savais pas s'il y avait un framework avec lequel travailler directement.

J'ai utilisé Juggernaut , mais il semble ne pas fonctionner avec redis-py dans la version actuelle, et Juggernaut a été déprécié récemment.

Quelqu'un a-t-il une suggestion avec mon cas?

46
little-eyes

Jetez un œil à Evénements envoyés par le serveur . Les événements envoyés par le serveur sont une API de navigateur qui vous permet de garder ouvert un socket pour votre serveur, en vous abonnant à un flux de mises à jour. Pour plus d'informations, lisez le message d'Alex MacCaw (Auteur de Juggernaut) sur pourquoi il tue juggernaut et pourquoi les événements plus simples envoyés par le serveur sont dans de nombreux cas le meilleur outil pour le travail que les Websockets.

Le protocole est vraiment simple. Ajoutez simplement le mimetype text/event-stream à votre réponse. Le navigateur gardera la connexion ouverte et écoutera les mises à jour. Un événement envoyé par le serveur est une ligne de texte commençant par data: et une nouvelle ligne suivante.

data: this is a simple message
<blank line>

Si vous souhaitez échanger des données structurées, videz simplement vos données en tant que json et envoyez le json sur le câble.

Un avantage est que vous pouvez utiliser SSE in Flask sans avoir besoin d'un serveur supplémentaire. Il y a un simple exemple d'application de chat sur github qui utilise redis comme backend 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")

Vous n'avez pas besoin d'utiliser gunicron pour exécuter l'exemple d'application. Assurez-vous simplement d'utiliser le thread lors de l'exécution de l'application, car sinon la connexion SSE bloquera votre serveur de développement:

if __== '__main__':
    app.debug = True
    app.run(threaded=True)

Du côté client, vous avez juste besoin d'une fonction de gestionnaire Javascript qui sera appelée lorsqu'un nouveau message est poussé depuis le serveur.

var source = new EventSource('/stream');
source.onmessage = function (event) {
     alert(event.data);
};

Les événements envoyés par le serveur sont pris en charge par Firefox récent, Chrome et navigateurs Safari. Internet Explorer ne prend pas encore en charge les événements envoyés par le serveur, mais devrait les prendre en charge dans la version 10. Il existe deux Polyfills recommandés pour prendre en charge les anciens navigateurs

90
Peter Hoffmann

Pour faire suite à @ la réponse de peter-hoffmann , j'ai écrit une extension Flask spécifiquement pour gérer les événements envoyés par le serveur. Elle s'appelle Flask -SSE , et c'est disponible sur PyPI . Pour l'installer, lancez:

$ pip install flask-sse

Vous pouvez l'utiliser comme ceci:

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!"

Et pour se connecter au flux d'événements à partir de Javascript, cela fonctionne comme ceci:

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);

La documentation est disponible sur ReadTheDocs. Notez que vous aurez besoin d'un serveur Redis en cours d'exécution pour gérer pub/sub.

11
singingwolfboy

Redis est exagéré: utilisez les événements côté serveur

Tard dans la soirée (comme d'habitude), mais à mon humble avis, utiliser Redis peut être exagéré.

Tant que vous travaillez en Python + Flask, pensez à utiliser les fonctions de générateur comme décrit dans cet excellent article de Panisuan Joe Chasinga . L'essentiel est:

Dans votre client 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>

Dans votre serveur 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")
10
fearless_fool