Redis
 sql >> Base de Dados >  >> NoSQL >> Redis

Como faço para retornar o frasco render_template após a conclusão do trabalho em segundo plano do Redis?

Uma solução básica, mas viável (essência):


Você pode fazer isso apenas redirecionando da rota que enfileira o trabalho e, em seguida, faça uma metatag atualizar essa página periodicamente. Primeiro importe as bibliotecas necessárias:
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

Configure as conexões relacionadas ao rq e defina a função a ser executada:
r = Redis(host='redisserver')
q = Queue(connection=r)

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

Em seguida, defina um modelo que possa atualizar a página a cada 5 segundos:
template_str='''<html>
    <head>
      {% if refresh %}
        <meta http-equiv="refresh" content="5">
      {% endif %}
    </head>
    <body>{{result}}</body>
    </html>'''

Também faremos uma função auxiliar para retornar esse template com uma variável inserida, usando flask render_template_string . Observe que o padrão de atualização é False, se não for fornecido:
def get_template(data, refresh=False):
    return render_template_string(template_str, result=data, refresh=refresh)

Agora faça uma rota que enfileirará nossa função, obtenha seu rq job-id e retorne um redirecionamento para o result view com esse id . Isso apenas leva a entrada na string de URL, mas pode obtê-la de qualquer lugar:
@app.route('/process/<string:data>')
def process(data):
    job = q.enqueue(slow_func, data)
    return redirect(url_for('result', id=job.id))

Agora vamos lidar com o resultado real, com a ajuda do rq.Job objeto. A lógica aqui pode ser ajustada, pois isso fará com que a página seja atualizada em todos os valores, exceto "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)

Se o status for "finished" então job.result conterá o valor de retorno de slow_func , então renderizamos isso na página.

Este método tem a desvantagem de causar vários pedidos ao servidor, enquanto aguarda a conclusão do trabalho. A metatag de atualização pode ser um pouco não convencional. Se você estiver enviando a solicitação de atualização do Javascript, existem soluções que podem enviar a solicitação AJAX em um intervalo, embora isso sofra do mesmo problema de várias solicitações.

A alternativa é usar websockets ou SSE para transmitir o resultado do trabalho concluído para o frontend assim que ele for concluído.

ATUALIZAÇÃO:27 de fevereiro de 2021

Decidi experimentar o método SSE de atualizar o frontend com o status do trabalho. Aprendi que rq tem suporte nativo para atualizar um meta atributo dentro do trabalho, importando rq.get_current_job dentro do trabalho, que pode ser acessado externamente após a atualização do trabalho.

Veja o código de demonstração para:

Um exemplo básico com uma barra de progresso (gist):