Redis
 sql >> Datenbank >  >> NoSQL >> Redis

Wie gebe ich Flask render_template zurück, nachdem der Redis-Hintergrundjob erledigt ist?

Eine einfache, aber praktikable Lösung (im Wesentlichen):

Sie könnten dies tun, indem Sie einfach von der Route umleiten, die den Job in die Warteschlange einreiht, und dann diese Seite regelmäßig durch ein Meta-Tag aktualisieren lassen. Importieren Sie zuerst die benötigten Bibliotheken:

from flask import Flask, redirect, url_for, render_template_string
app = Flask(__name__)

from time import sleep

from rq import Queue
from rq.job import Job
from redis import Redis

Richten Sie die rq-bezogenen Verbindungen ein und definieren Sie die auszuführende Funktion:

r = Redis(host='redisserver')
q = Queue(connection=r)

def slow_func(data):
    sleep(5)
    return 'Processed %s' % (data,)

Definieren Sie dann eine Vorlage, die die Seite alle 5 Sekunden aktualisieren kann:

template_str='''<html>
    <head>
      {% if refresh %}
        <meta http-equiv="refresh" content="5">
      {% endif %}
    </head>
    <body>{{result}}</body>
    </html>'''

Wir werden auch eine Hilfsfunktion erstellen, um diese Vorlage mit einer eingefügten Variablen zurückzugeben, indem wir die Flasche render_template_string verwenden . Beachten Sie, dass die Aktualisierung standardmäßig auf False gesetzt wird, wenn sie nicht angegeben wird:

def get_template(data, refresh=False):
    return render_template_string(template_str, result=data, refresh=refresh)

Erstellen Sie nun eine Route, die unsere Funktion in die Warteschlange einreiht, erhalten Sie ihre rq-Job-ID und geben Sie dann eine Umleitung zum result zurück Ansicht mit dieser id . Dies nimmt nur eine Eingabe in die URL-Zeichenfolge, könnte diese aber von überall her bekommen:

@app.route('/process/<string:data>')
def process(data):
    job = q.enqueue(slow_func, data)
    return redirect(url_for('result', id=job.id))

Lassen Sie uns nun mit Hilfe des rq.Job das eigentliche Ergebnis handhaben Objekt. Die Logik hier könnte optimiert werden, da dies eine Seitenaktualisierung für alle Werte außer "finished" bewirkt :

@app.route('/result/<string:id>')
def result(id):
    job = Job.fetch(id, connection=r)
    status = job.get_status()
    if status in ['queued', 'started', 'deferred', 'failed']:
        return get_template(status, refresh=True)
    elif status == 'finished':
        result = job.result 
        # If this is a string, we can simply return it:
        return get_template(result)

Wenn der Status "finished" ist dann job.result enthält den Rückgabewert von slow_func , also rendern wir das auf der Seite.

Diese Methode hat den Nachteil, dass mehrere Anfragen an den Server gestellt werden, während auf den Abschluss des Jobs gewartet wird. Das Meta-Refresh-Tag ist vielleicht etwas unkonventionell. Wenn Sie die Anforderung für ein Update von Javascript aus senden, gibt es Lösungen, die die AJAX-Anforderung in einem Intervall senden können, obwohl dies unter demselben Problem mit mehreren Anforderungen leidet.

Die Alternative besteht darin, Websockets oder SSE zu verwenden, um das Ergebnis des abgeschlossenen Jobs an das Frontend zu streamen, sobald es abgeschlossen ist.

AKTUALISIERUNG:27. Februar 2021

Ich habe mich entschieden, die SSE-Methode zum Aktualisieren des Frontends mit dem Jobstatus auszuprobieren. Ich habe gelernt, dass rq hat native Unterstützung für die Aktualisierung eines meta -Attribut innerhalb des Jobs, indem Sie rq.get_current_job importieren innerhalb des Jobs, auf die dann nach der Jobaktualisierung extern zugegriffen werden kann.

Siehe Demonstrationscode für:

Ein einfaches Beispiel mit einem Fortschrittsbalken (Gist):