Podstawowe, ale wykonalne rozwiązanie (istota):
Możesz to zrobić, po prostu przekierowując z trasy, która umieszcza zadanie w kolejce, a następnie okresowo odświeżaj tę stronę za pomocą metatagu. Najpierw zaimportuj wymagane biblioteki:
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
Skonfiguruj połączenia związane z rq i zdefiniuj funkcję do uruchomienia:
r = Redis(host='redisserver')
q = Queue(connection=r)
def slow_func(data):
sleep(5)
return 'Processed %s' % (data,)
Następnie zdefiniuj szablon, który może odświeżać stronę co 5 sekund:
template_str='''<html>
<head>
{% if refresh %}
<meta http-equiv="refresh" content="5">
{% endif %}
</head>
<body>{{result}}</body>
</html>'''
Stworzymy również funkcję pomocniczą, aby zwrócić ten szablon z wstawioną zmienną, używając flask render_template_string
. Zwróć uwagę, że odświeżanie jest domyślnie ustawione na False, jeśli nie podano:
def get_template(data, refresh=False):
return render_template_string(template_str, result=data, refresh=refresh)
Teraz utwórz trasę, która zakolejkuje naszą funkcję, pobierz jej identyfikator zadania rq, a następnie zwróci przekierowanie do result
widok z tym id
. To po prostu pobiera dane wejściowe w ciągu adresu URL, ale można je uzyskać z dowolnego miejsca:
@app.route('/process/<string:data>')
def process(data):
job = q.enqueue(slow_func, data)
return redirect(url_for('result', id=job.id))
Teraz zajmijmy się rzeczywistym wynikiem za pomocą rq.Job
obiekt. Logika tutaj może zostać zmodyfikowana, ponieważ spowoduje to odświeżenie strony na wszystkich wartościach z wyjątkiem "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)
Jeśli stan to "finished"
następnie job.result
będzie zawierać wartość zwracaną przez slow_func
, więc renderujemy to na stronie.
Ta metoda ma tę wadę, że powoduje kilka żądań do serwera podczas oczekiwania na zakończenie zadania. Meta tag odświeżania może być nieco niekonwencjonalny. Jeśli wysyłasz żądanie aktualizacji z JavaScript, istnieją rozwiązania, które mogą wysyłać żądanie AJAX w określonych odstępach czasu, chociaż dotyczy to tego samego problemu z wieloma żądaniami.
Alternatywą jest użycie gniazd sieciowych lub SSE do strumieniowego przesyłania wyników ukończonego zadania do interfejsu użytkownika, gdy tylko się zakończy.
AKTUALIZACJA:27 lutego 2021
Postanowiłem wypróbować metodę SSE polegającą na aktualizacji frontendu o status zadania. Dowiedziałem się, że rq
ma natywną obsługę aktualizacji meta
atrybut w ramach zadania, importując rq.get_current_job
wewnątrz zadania, do którego można uzyskać dostęp z zewnątrz po odświeżeniu zadania.
Zobacz kod demonstracyjny dla:
Podstawowy przykład z paskiem postępu (istota):