Cómo usar la lista de tareas en cola de App Engine (tareas de extracción) en las apps de Flask (módulo 18)

1. Descripción general

El objetivo de la serie de codelabs de Serverless Migration Station (instructivos prácticos y de autoaprendizaje) y los videos relacionados es ayudar a los desarrolladores de Google Cloud sin servidores a modernizar sus aplicaciones guiándolos a través de una o más migraciones, principalmente alejándose de los servicios heredados. De esta manera, tus apps serán más portátiles y tendrás más opciones y flexibilidad, lo que te permitirá integrar y acceder a una mayor variedad de productos de Cloud, y actualizarte más fácilmente a versiones de lenguaje más recientes. Si bien, en un principio, se enfoca en los primeros usuarios de Cloud, principalmente los desarrolladores de App Engine (entorno estándar), esta serie es lo suficientemente amplia como para incluir otras plataformas sin servidores, como Cloud Functions y Cloud Run, o en cualquier otro lugar si corresponde.

En este codelab, aprenderás a incluir y usar tareas de extracción de la lista de tareas en cola de App Engine en la app de muestra del codelab del módulo 1. Agregamos su uso de tareas de extracción en este instructivo del módulo 18 y, luego, migramos ese uso a Cloud Pub/Sub en el módulo 19. Quienes usen Task Queues para tareas de envío migrarán a Cloud Tasks y deberán consultar los módulos del 7 al 9.

En un próximo lab,

  • Usa la API o el servicio agrupado de App Engine Task Queue
  • Agrega el uso de la cola de extracción a una app básica de Flask App Engine NDB de Python 2

Requisitos

Encuesta

¿Cómo usarás este instructivo?

Leer Leer y completar los ejercicios

¿Cómo calificarías tu experiencia en Python?

Principiante Intermedio Avanzado

¿Cómo calificarías tu experiencia en el uso de los servicios de Google Cloud?

Principiante Intermedio Avanzado

2. Fondo

Para migrar desde las tareas de extracción de la lista de tareas en cola de App Engine, agrega su uso a la app existente de Flask y App Engine NDB que se generó en el codelab del módulo 1. La app de ejemplo muestra las visitas más recientes al usuario final. Está bien, pero es aún más interesante hacer un seguimiento de los visitantes para ver quiénes son los que más visitan el sitio.

Si bien podríamos usar tareas push para estos recuentos de visitantes, queremos dividir la responsabilidad entre la app de ejemplo, cuyo trabajo es registrar visitas y responder de inmediato a los usuarios, y un "trabajador" designado, cuyo trabajo es registrar los recuentos de visitantes fuera del flujo de trabajo normal de solicitud-respuesta.

Para implementar este diseño, agregaremos el uso de colas de extracción a la aplicación principal y admitiremos la funcionalidad del trabajador. El trabajador puede ejecutarse como un proceso independiente (como una instancia de backend o código que se ejecuta en una VM que siempre está activa), un trabajo cron o una solicitud HTTP básica de línea de comandos con curl o wget. Después de esta integración, podrás migrar la app a Cloud Pub/Sub en el siguiente codelab (módulo 19).

En este instructivo, se incluyen los siguientes pasos:

  1. Configurar/trabajo previo
  2. Actualizar configuración
  3. Modifica el código de la aplicación

3. Configurar/trabajo previo

Esta sección explica cómo:

  1. Configura el proyecto de Cloud
  2. Obtén app de ejemplo del modelo de referencia
  3. (Vuelve a) implementar y validar la app de referencia

Estos pasos garantizan que comiences con código funcional.

1. Configura el proyecto

Si completaste el codelab del módulo 1, vuelve a usar ese mismo proyecto (y el código). De manera alternativa, puedes crear un proyecto nuevo o reutilizar otro proyecto existente. Asegúrate de que el proyecto tenga una cuenta de facturación activa y una app de App Engine habilitada. Busca el ID de tu proyecto, ya que lo necesitarás varias veces en este codelab y lo usarás cada vez que encuentres la variable PROJECT_ID.

2. Obtén app de ejemplo del modelo de referencia

Uno de los requisitos previos para este codelab es tener una app de App Engine del módulo 1 que funcione. Completa el codelab del módulo 1 (recomendado) o copia la app del módulo 1 del repo. Sin importar si usas el tuyo o el nuestro, el código del módulo 1 es el que tendremos en cuenta. Este codelab te guiará en cada paso y concluirá con un código que se parezca al que se encuentra en la carpeta del repositorio del módulo 18 "FINISH".

Independientemente de la app del módulo 1 que uses, la carpeta debería verse como el siguiente resultado, posiblemente con una carpeta lib también:

$ ls
README.md               appengine_config.py     requirements.txt
app.yaml                main.py                 templates

3. (vuelve a) implementa la aplicación de modelo de referencia

Ejecuta los siguientes pasos para implementar la app del Módulo 1:

  1. Borra la carpeta lib si hay una y ejecuta pip install -t lib -r requirements.txt para volver a completar lib. Si tienes instalados Python 2 y 3, es posible que debas usar el comando pip2.
  2. Asegúrate de haber instalado y inicializado la herramienta de línea de comandos de gcloud, y de haber revisado su uso.
  3. Configura tu proyecto de Cloud con gcloud config set project PROJECT_ID si no quieres ingresar tu PROJECT_ID con cada comando gcloud que se emita.
  4. Implementa la app de ejemplo con gcloud app deploy
  5. Confirma que la app del módulo 1 se ejecuta según lo previsto y muestra las visitas más recientes (como se ilustra a continuación).

a7a9d2b80d706a2b.png

4. Actualizar configuración

No es necesario realizar cambios en los archivos de configuración estándar de App Engine (app.yaml, requirements.txt, appengine_config.py). En cambio, agrega un nuevo archivo de configuración, queue.yaml, con el siguiente contenido y colócalo en el mismo directorio de nivel superior:

queue:
- name: pullq
  mode: pull

El archivo queue.yaml especifica todas las colas de tareas que existen para tu app (excepto la cola default [de envío] que App Engine crea automáticamente). En este caso, solo hay una, una cola de extracción llamada pullq. App Engine requiere que la directiva mode se especifique como pull; de lo contrario, crea una cola de envío de forma predeterminada. Obtén más información para crear colas de extracción en la documentación. Consulta también la página de referencia de queue.yaml para ver otras opciones.

Implementa este archivo por separado de tu app. Seguirás usando gcloud app deploy, pero también proporcionarás queue.yaml en la línea de comandos:

$ gcloud app deploy queue.yaml
Configurations to update:

descriptor:      [/tmp/mod18-gaepull/queue.yaml]
type:            [task queues]
target project:  [my-project]

WARNING: Caution: You are updating queue configuration. This will override any changes performed using 'gcloud tasks'. More details at
https://cloud.google.com/tasks/docs/queue-yaml

Do you want to continue (Y/n)?

Updating config [queue]...⠹WARNING: We are using the App Engine app location (us-central1) as the default location. Please use the "--location" flag if you want to use a different location.
Updating config [queue]...done.

Task queues have been updated.

Visit the Cloud Platform Console Task Queues page to view your queues and cron jobs.
$

5. Modifica el código de la aplicación

En esta sección, se incluyen actualizaciones de los siguientes archivos:

  • main.py: Agrega el uso de listas de extracción a la aplicación principal.
  • templates/index.html: Actualiza la plantilla web para mostrar los datos nuevos.

Importaciones y constantes

El primer paso es agregar una nueva importación y varias constantes para admitir las colas de extracción:

  • Agrega una importación de la biblioteca de Task Queue, google.appengine.api.taskqueue.
  • Agrega tres constantes para admitir el alquiler de la cantidad máxima de tareas de extracción (TASKS) durante una hora (HOUR) desde nuestra cola de extracción (QUEUE).
  • Agrega una constante para mostrar las visitas más recientes y los visitantes principales (LIMIT).

A continuación, se muestra el código original y cómo se ve después de realizar estas actualizaciones:

ANTES:

from flask import Flask, render_template, request
from google.appengine.ext import ndb

app = Flask(__name__)

DESPUÉS:

from flask import Flask, render_template, request
from google.appengine.api import taskqueue
from google.appengine.ext import ndb

HOUR = 3600
LIMIT = 10
TASKS = 1000
QNAME = 'pullq'
QUEUE = taskqueue.Queue(QNAME)
app = Flask(__name__)

Agrega una tarea de extracción (recopila datos para la tarea y crea la tarea en la cola de extracción)

El modelo de datos Visit sigue siendo el mismo, al igual que la consulta de visitas para mostrar en fetch_visits(). El único cambio necesario en esta parte del código se encuentra en store_visit(). Además de registrar la visita, agrega una tarea a la cola de extracción con la dirección IP del visitante para que el trabajador pueda incrementar el contador de visitantes.

ANTES:

class Visit(ndb.Model):
    'Visit entity registers visitor IP address & timestamp'
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

def store_visit(remote_addr, user_agent):
    'create new Visit entity in Datastore'
    Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

def fetch_visits(limit):
    'get most recent visits'
    return Visit.query().order(-Visit.timestamp).fetch(limit)

DESPUÉS:

class Visit(ndb.Model):
    'Visit entity registers visitor IP address & timestamp'
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

def store_visit(remote_addr, user_agent):
    'create new Visit in Datastore and queue request to bump visitor count'
    Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()
    QUEUE.add(taskqueue.Task(payload=remote_addr, method='PULL'))

def fetch_visits(limit):
    'get most recent visits'
    return Visit.query().order(-Visit.timestamp).fetch(limit)

Crea un modelo de datos y una función de consulta para el seguimiento de visitantes

Agrega un modelo de datos VisitorCount para hacer un seguimiento de los visitantes. Debe tener campos para el visitor en sí, así como un número entero counter para hacer un seguimiento de la cantidad de visitas. Luego, agrega una función nueva (o, de manera alternativa, puede ser un classmethod de Python) llamada fetch_counts()para consultar y devolver los principales visitantes en orden de mayor a menor. Agrega la clase y la función justo debajo del cuerpo de fetch_visits():

class VisitorCount(ndb.Model):
    visitor = ndb.StringProperty(repeated=False, required=True)
    counter = ndb.IntegerProperty()

def fetch_counts(limit):
    'get top visitors'
    return VisitCount.query().order(-VisitCount.counter).fetch(limit)

Agrega código de trabajador

Agrega una nueva función log_visitors() para registrar a los visitantes a través de una solicitud GET a /log. Utiliza un diccionario o hash para hacer un seguimiento de los recuentos de visitantes más recientes y alquila tantas tareas como sea posible durante una hora. Para cada tarea, se registran todas las visitas del mismo visitante. Con los recuentos en mano, la app actualiza todas las entidades VisitorCount correspondientes que ya están en Datastore o crea nuevas si es necesario. El último paso devuelve un mensaje de texto sin formato que indica cuántos visitantes se registraron a partir de cuántas tareas procesadas. Agrega esta función a main.py justo debajo de fetch_counts():

@app.route('/log')
def log_visitors():
    'worker processes recent visitor counts and updates them in Datastore'
    # tally recent visitor counts from queue then delete those tasks
    tallies = {}
    tasks = QUEUE.lease_tasks(HOUR, TASKS)
    for task in tasks:
        visitor = task.payload
        tallies[visitor] = tallies.get(visitor, 0) + 1
    if tasks:
        QUEUE.delete_tasks(tasks)

    # increment those counts in Datastore and return
    for visitor in tallies:
        counter = VisitorCount.query(VisitorCount.visitor == visitor).get()
        if not counter:
            counter = VisitorCount(visitor=visitor, counter=0)
            counter.put()
        counter.counter += tallies[visitor]
        counter.put()
    return 'DONE (with %d task[s] logging %d visitor[s])\r\n' % (
            len(tasks), len(tallies))

Actualiza el controlador principal con los nuevos datos de visualización

Para mostrar los visitantes principales, actualiza el controlador principal root() para invocar fetch_counts(). Además, la plantilla se actualizará para mostrar la cantidad de visitantes principales y las visitas más recientes. Empaqueta los recuentos de visitantes junto con las visitas más recientes de la llamada a fetch_visits() y colócalos en un solo context para pasarlos a la plantilla web. A continuación, se muestra el código antes y después de realizar este cambio:

ANTES:

@app.route('/')
def root():
    'main application (GET) handler'
    store_visit(request.remote_addr, request.user_agent)
    visits = fetch_visits(10)
    return render_template('index.html', visits=visits)

DESPUÉS:

@app.route('/')
def root():
    'main application (GET) handler'
    store_visit(request.remote_addr, request.user_agent)
    context = {
        'limit':  LIMIT,
        'visits': fetch_visits(LIMIT),
        'counts': fetch_counts(LIMIT),
    }
    return render_template('index.html', **context)

Estos son todos los cambios que requiere main.py. A continuación, se muestra una representación gráfica de esas actualizaciones con fines ilustrativos para que tengas una idea general de los cambios que realizarás en main.py:

ad5fd3345efc13d0.png

Actualiza la plantilla web con los nuevos datos de la pantalla

La plantilla web templates/index.html requiere una actualización para mostrar los visitantes principales, además de la carga útil normal de los visitantes más recientes. Coloca los visitantes principales y sus recuentos en una tabla en la parte superior de la página y sigue renderizando las visitas más recientes como antes. El único otro cambio es especificar el número que se muestra a través de la variable limit en lugar de codificarlo de forma rígida. Estas son las actualizaciones que debes realizar en tu plantilla web:

ANTES:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
<body>

<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

DESPUÉS:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
<body>

<h1>VisitMe example</h1>

<h3>Top {{ limit }} visitors</h3>
<table border=1 cellspacing=0 cellpadding=2>
    <tr><th>Visitor</th><th>Visits</th></tr>
{% for count in counts %}
    <tr><td>{{ count.visitor|e }}</td><td align="center">{{ count.counter }}</td></tr>
{% endfor %}
</table>

<h3>Last {{ limit }} visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

Con esto, se completan los cambios necesarios para agregar el uso de tareas de extracción de la cola de tareas de App Engine a la app de muestra del módulo 1. Tu directorio ahora representa la app de muestra del módulo 18 y debería contener estos archivos:

$ ls
README.md               appengine_config.py     queue.yaml              templates
app.yaml                main.py                 requirements.txt

6. Resumen/Limpieza

En esta sección, se completa el codelab con la implementación de la app y la verificación de que funcione según lo previsto y en cualquier resultado reflejado. Ejecuta el trabajador por separado para procesar los recuentos de visitantes. Después de la validación de la app, realiza los pasos de limpieza necesarios y considera los próximos pasos.

Implementa y verifica la aplicación

Asegúrate de que ya configuraste tu cola de extracción como lo hicimos cerca de la parte superior de este codelab con gcloud app deploy queue.yaml. Si ya completaste ese paso y tu app de ejemplo está lista para usarse, impleméntala con gcloud app deploy. El resultado debería ser idéntico al de la app del Módulo 1, excepto que ahora incluye una tabla de "visitantes principales" en la parte superior:

b667551dcbab1a09.png

Si bien el frontend web actualizado muestra los visitantes principales y las visitas más recientes, ten en cuenta que los recuentos de visitantes no incluyen esta visita. La app muestra los recuentos de visitantes anteriores mientras descarta una nueva tarea que incrementa el recuento de este visitante en la cola de extracción, una tarea que está esperando a ser procesada.

Puedes ejecutar la tarea llamando a /log de varias maneras:

Por ejemplo, si usas curl para enviar una solicitud GET a /log, el resultado se vería de la siguiente manera, dado que proporcionaste tu PROJECT_ID:

$ curl https://PROJECT_ID.appspot.com/log
DONE (with 1 task[s] logging 1 visitor[s])

La cantidad actualizada se reflejará en la próxima visita al sitio web. Eso es todo.

Felicitaciones por completar este codelab para agregar el uso del servicio de colas de extracción de App Engine Task Queue a la app de ejemplo correctamente. Ahora está lista para migrar a Cloud Pub/Sub, Cloud NDB y Python 3 en el módulo 19.

Limpia

General

Si terminaste por ahora, te recomendamos que inhabilites tu app de App Engine para evitar incurrir en cargos de facturación. Sin embargo, si deseas realizar más pruebas o experimentos, la plataforma de App Engine tiene una cuota gratuita, por lo que no se te cobrará siempre que no excedas ese nivel de uso. Esto se aplica a la capacidad de procesamiento, pero también puede haber cargos por los servicios relevantes de App Engine, por lo que debes consultar su página de precios para obtener más información. Si esta migración involucra otros servicios de Cloud, estos se facturarán por separado. En cualquier caso, si corresponde, consulta la sección "Específico para este codelab" que se encuentra más abajo.

Para divulgar toda la información, la implementación en una plataforma de computación sin servidores de Google Cloud, como App Engine, genera costos menores de compilación y almacenamiento. Cloud Build tiene su propia cuota gratuita, al igual que Cloud Storage. El almacenamiento de esa imagen usa parte de esa cuota. Sin embargo, es posible que vivas en una región que no tenga ese nivel gratuito, por lo que debes tener en cuenta tu uso de almacenamiento para minimizar los costos potenciales. Las "carpetas" específicas de Cloud Storage que debes revisar incluyen las siguientes:

  • console.cloud.google.com/storage/browser/LOC.artifacts.PROJECT_ID.appspot.com/containers/images
  • console.cloud.google.com/storage/browser/staging.PROJECT_ID.appspot.com
  • Los vínculos de almacenamiento anteriores dependen de tu PROJECT_ID y *LOC*ación, por ejemplo, "us" si tu app está alojada en EE.UU.

Por otro lado, si no vas a continuar con esta aplicación ni con otros codelabs de migración relacionados y quieres borrar todo por completo, cierra tu proyecto.

Específico para este codelab

Los servicios que se indican a continuación son exclusivos de este codelab. Consulta la documentación de cada producto para obtener más información:

Próximos pasos

En esta "migración", agregaste el uso de la cola de envío de Task Queue a la app de muestra del módulo 1, ya que agregaste compatibilidad para hacer un seguimiento de los visitantes y, de este modo, implementaste la app de muestra del módulo 18. En la próxima migración, actualizarás las tareas de extracción de App Engine a Cloud Pub/Sub. Desde fines de 2021, los usuarios ya no están obligados a migrar a Cloud Pub/Sub cuando actualizan a Python 3. Obtén más información sobre este tema en la siguiente sección.

Para migrar a Cloud Pub/Sub, consulta el codelab del módulo 19. Además de eso, hay migraciones adicionales que se deben tener en cuenta, como Cloud Datastore, Cloud Memorystore, Cloud Storage o Cloud Tasks (listas de aplicaciones en cola). También hay migraciones entre productos a Cloud Run y Cloud Functions. Se puede acceder a todo el contenido de Serverless Migration Station (codelabs, videos y código fuente [cuando esté disponible]) en su repositorio de código abierto.

7. Migración a Python 3

En el otoño de 2021, el equipo de App Engine extendió la compatibilidad de muchos de los servicios en paquetes a los entornos de ejecución de segunda generación (que tienen un entorno de ejecución de primera generación). Como resultado, ya no es necesario que migres de servicios agrupados, como App Engine Task Queue, a servicios independientes de Cloud o de terceros, como Cloud Pub/Sub, cuando transfieras tu app a Python 3. En otras palabras, puedes seguir usando Task Queue en las apps de App Engine con Python 3 siempre y cuando adaptes el código para acceder a los servicios agrupados en paquetes desde los entornos de ejecución de próxima generación.

Puedes obtener más información para migrar el uso de los servicios agrupados a Python 3 en el Codelab del módulo 17 y su video correspondiente. Si bien ese tema está fuera del alcance del módulo 18, a continuación se vinculan las versiones de Python 3 de la app del módulo 1 que se transfirieron a Python 3 y que aún usan App Engine NDB. (En algún momento, también estará disponible una versión en Python 3 de la app del módulo 18).

8. Recursos adicionales

A continuación, se enumeran recursos adicionales para los desarrolladores que deseen explorar más a fondo este módulo de migración o uno relacionado, así como los productos relacionados. Esto incluye lugares para proporcionar comentarios sobre este contenido, vínculos al código y varios documentos que pueden resultarte útiles.

Problemas o comentarios de los codelabs

Si encuentras algún problema con este Codelab, primero busca el problema antes de enviarlo. Vínculos para buscar y crear problemas nuevos:

Recursos de migración

En la siguiente tabla, puedes encontrar vínculos a las carpetas del repositorio para el módulo 1 (INICIAR) y el módulo 18 (FINALIZAR). También se puede acceder a ellos desde el repositorio de todas las migraciones de codelab de App Engine. Clónalo o descarga un archivo ZIP.

Codelab

Python 2

Python 3

Módulo 1

código

code (no se incluye en este instructivo)

Módulo 18 (este codelab)

código

N/A

Referencias en línea

A continuación, se incluyen recursos pertinentes para este instructivo:

Lista de tareas en cola de App Engine

Plataforma de App Engine

Documentación de App Engine

Tiempo de ejecución de Python 2 App Engine (entorno estándar)

Tiempo de ejecución de Python 3 de App Engine (entorno estándar)

Diferencias entre los entornos de ejecución de Python 2 y 3 de App Engine (entorno estándar)

Guía de migración de Python 2 a 3 de App Engine (entorno estándar)

Información sobre los precios y las cuotas de App Engine

Lanzamiento de la plataforma App Engine de segunda generación (2018)

Asistencia a largo plazo para entornos de ejecución heredados

Ejemplos de migración de documentación

Otra información de la nube

Videos

Licencia

Este trabajo cuenta con una licencia Atribución 2.0 Genérica de Creative Commons.