sql >> Databasteknik >  >> NoSQL >> Redis

Hur returnerar jag flask render_template efter att Redis bakgrundsjobb är gjort?

En grundläggande men fungerande lösning (konsekvens):

Du kan göra detta genom att bara omdirigera från rutten som ställer jobbet i kö och sedan låta en metatagg uppdatera sidan med jämna mellanrum. Importera först de nödvändiga biblioteken:

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

Ställ in de rq-relaterade anslutningarna och definiera funktionen som ska köras:

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

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

Definiera sedan en mall som kan uppdatera sidan var 5:e sekund:

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

Vi kommer också att skapa en hjälpfunktion för att returnera den mallen med en variabel insatt, med hjälp av kolven render_template_string . Observera att uppdatering är som standard till False, om det inte finns:

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

Gör nu en rutt som ställer vår funktion i kö, hämta dess rq jobb-id och returnera sedan en omdirigering till result visa med det id . Detta tar bara input i URL-strängen, men kan hämta det var som helst:

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

Låt oss nu hantera det faktiska resultatet, med hjälp av rq.Job objekt. Logiken här kan justeras, eftersom det kommer att orsaka en siduppdatering på alla värden utom "finished" :

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

Om statusen är "finished" sedan job.result kommer att innehålla returvärdet för slow_func , så vi återger detta på sidan.

Denna metod har nackdelen att den orsakar flera förfrågningar till servern i väntan på att jobbet ska slutföras. Meta refresh-taggen kan vara lite okonventionell. Om du skickar begäran om en uppdatering från Javascript finns det lösningar som kan skicka AJAX-förfrågan med ett intervall, även om detta lider av samma problem med flera begäranden.

Alternativet är att använda websockets eller SSE för att streama resultatet av det avslutade jobbet till frontend så snart det är klart.

UPPDATERING:27 februari 2021

Jag bestämde mig för att prova SSE-metoden för att uppdatera frontend med jobbstatus. Jag lärde mig att rq har inbyggt stöd för att uppdatera en meta attribut i jobbet, genom att importera rq.get_current_job inuti jobbet, som sedan kan nås externt efter jobbuppdatering.

Se demonstrationskoden för:

Ett grundläggande exempel med en förloppsindikator (gist):




  1. Hur återanvänder man redis-anslutning i socket.io?

  2. Återställ från avbruten anslutning i redis pub/sub

  3. Mongodb uppdaterar djupt kapslade underdokument

  4. Dra och lägg till samtidigt med mongo