Cómo usar la lista de tareas en cola de App Engine (tareas push) en las apps de Flask (módulo 7)

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 usar las tareas de envío de la cola de tareas de App Engine en la app de ejemplo del codelab del módulo 1. La entrada de blog y el video del módulo 7 complementan este instructivo y proporcionan una breve descripción general del contenido.

En este módulo, agregaremos el uso de tareas de envío y, luego, migraremos ese uso a Cloud Tasks en el módulo 8 y, más adelante, a Python 3 y Cloud Datastore en el módulo 9. Quienes usen Task Queues para tareas de extracción migrarán a Cloud Pub/Sub y deberán consultar los módulos 18 y 19.

En un próximo lab,

  • Usa la API o el servicio agrupado de App Engine Task Queue
  • Agrega el uso de tareas de envío a una app básica de Flask App Engine NDB escrita en 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

La lista de tareas en cola de App Engine admite tareas de envío y de extracción. Para mejorar la portabilidad de las aplicaciones, el equipo de Google Cloud recomienda migrar de los servicios heredados agrupados en paquetes, como Task Queue, a otros servicios independientes de Cloud o servicios equivalentes de terceros.

La migración de tareas de extracción se aborda en los módulos de migración 18 y 19, mientras que los módulos 7 a 9 se centran en la migración de tareas de envío. Para migrar desde las tareas de envío de App Engine Task Queue, agrega su uso a la app existente de Flask y App Engine NDB que se generó en el codelab del módulo 1. En esa app, una nueva vista de página registra una nueva visita y muestra las visitas más recientes al usuario. Dado que las visitas más antiguas nunca se vuelven a mostrar y ocupan espacio en Datastore, crearemos una tarea de envío para borrar automáticamente las visitas más antiguas. En el módulo 8, migraremos esa app de Task Queue a Cloud Tasks.

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, te recomendamos volver 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 que App Engine esté habilitado.

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 7 "FINISH".

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

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

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

Ejecuta los siguientes pasos para volver a 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 sin problemas para mostrar 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).

5. Modifica los archivos de la aplicación

El archivo principal de la aplicación es main.py, y todas las actualizaciones de esta sección se relacionan con ese archivo. También hay una actualización menor en la plantilla web, templates/index.html. Estos son los cambios que debes implementar en esta sección:

  1. Actualiza las importaciones
  2. Agregar tarea de envío
  3. Agregar controlador de tareas
  4. Actualiza la plantilla web

1. Actualiza las importaciones

Una importación de google.appengine.api.taskqueue incorpora la funcionalidad de Task Queue. También se requieren algunos paquetes de la biblioteca estándar de Python:

  • Como agregaremos una tarea para borrar las visitas más antiguas, la app deberá controlar las marcas de tiempo, lo que significa que deberá usar time y datetime.
  • Para registrar información útil sobre la ejecución de tareas, necesitamos logging.

Después de agregar todas estas importaciones, tu código se verá de la siguiente manera antes y después de estos cambios:

ANTES:

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

DESPUÉS:

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

2. Agregar tarea de envío (recopilar datos para la tarea, poner en cola la tarea nueva)

En la documentación de la cola de envío, se indica lo siguiente: "Para procesar una tarea, debes agregarla a una cola de envío. App Engine proporciona una lista de aplicaciones en cola predeterminada llamada default, que está configurada y lista para usar con la configuración predeterminada. Si quieres, puedes agregar todas tus tareas a la cola predeterminada, sin necesidad de crear ni configurar otras. En este codelab, se usa la cola default para mayor brevedad. Para obtener más información sobre cómo definir tus propias colas de envío con las mismas características o diferentes, consulta la documentación sobre cómo crear colas de envío.

El objetivo principal de este codelab es agregar una tarea (a la cola de envío de default) cuyo trabajo sea borrar las visitas antiguas de Datastore que ya no se muestran. La app de referencia registra cada visita (solicitud GET a /) creando una nueva entidad Visit y, luego, recupera y muestra las visitas más recientes. Ninguna de las visitas más antiguas se volverá a mostrar ni a usar, por lo que la tarea de envío borra todas las visitas más antiguas que la más antigua que se muestra. Para lograr esto, el comportamiento de la app debe cambiar un poco:

  1. Cuando consultes las visitas más recientes, en lugar de devolverlas de inmediato, modifica la app para guardar la marca de tiempo del Visit más reciente, el más antiguo que se muestra. Es seguro borrar todas las visitas anteriores a esta.
  2. Crea una tarea de envío con esta marca de tiempo como carga útil y dirígela al controlador de tareas, al que se puede acceder a través de un POST HTTP a /trim. Específicamente, usa utilidades estándar de Python para convertir la marca de tiempo de Datastore y enviarla (como un número de punto flotante) a la tarea, pero también regístrala (como una cadena) y devuelve esa cadena como un valor centinela para mostrarle al usuario.

Todo esto sucede en fetch_visits(), y así se ve antes y después de realizar estas actualizaciones:

ANTES:

def fetch_visits(limit):
    return (v.to_dict() for v in Visit.query().order(
            -Visit.timestamp).fetch(limit))

DESPUÉS:

def fetch_visits(limit):
    'get most recent visits and add task to delete older visits'
    data = Visit.query().order(-Visit.timestamp).fetch(limit)
    oldest = time.mktime(data[-1].timestamp.timetuple())
    oldest_str = time.ctime(oldest)
    logging.info('Delete entities older than %s' % oldest_str)
    taskqueue.add(url='/trim', params={'oldest': oldest})
    return (v.to_dict() for v in data), oldest_str

3. Agregar un controlador de tareas (código que se llama cuando se ejecuta la tarea)

Si bien el borrado de visitas antiguas podría haberse logrado fácilmente en fetch_visits(), ten en cuenta que esta funcionalidad no tiene mucho que ver con el usuario final. Es una función auxiliar y un buen candidato para procesarse de forma asíncrona fuera de las solicitudes estándar de la app. El usuario final se beneficiará de consultas más rápidas porque habrá menos información en Datastore. Crea una función nueva trim(), a la que se llama a través de una solicitud de POST de Task Queue a /trim, que hace lo siguiente:

  1. Extrae la carga útil de la marca de tiempo de la "visita más antigua".
  2. Emite una consulta de Datastore para encontrar todas las entidades anteriores a esa marca de tiempo.
  3. Opta por una consulta más rápida "solo de claves" porque no se necesitan datos reales del usuario.
  4. Registra la cantidad de entidades que se borrarán (incluido el cero).
  5. Llamadas a ndb.delete_multi() para borrar las entidades (se omiten si no hay).
  6. Devuelve una cadena vacía (junto con un código de devolución HTTP 200 implícito).

Puedes ver todo eso en trim() a continuación. Agrégalo a main.py justo después de fetch_visits():

@app.route('/trim', methods=['POST'])
def trim():
    '(push) task queue handler to delete oldest visits'
    oldest = request.form.get('oldest', type=float)
    keys = Visit.query(
            Visit.timestamp < datetime.fromtimestamp(oldest)
    ).fetch(keys_only=True)
    nkeys = len(keys)
    if nkeys:
        logging.info('Deleting %d entities: %s' % (
                nkeys, ', '.join(str(k.id()) for k in keys)))
        ndb.delete_multi(keys)
    else:
        logging.info('No entities older than: %s' % time.ctime(oldest))
    return ''   # need to return SOME string w/200

4. Actualiza la plantilla web

Actualiza la plantilla web, templates/index.html, con esta condición de Jinja2 para mostrar la marca de tiempo más antigua si existe esa variable:

{% if oldest is defined %}
    <b>Deleting visits older than:</b> {{ oldest }}</p>
{% endif %}

Agrega este fragmento después de la lista de visitas que se muestra, pero antes de cerrar el cuerpo, de modo que tu plantilla se vea de la siguiente manera:

<!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>

{% if oldest is defined %}
    <b>Deleting visits older than:</b> {{ oldest }}</p>
{% endif %}
</body>
</html>

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. Después de la validación de la app, realiza cualquier limpieza y considera los próximos pasos.

Implementa y verifica la aplicación

Implementa la app con gcloud app deploy. El resultado debería ser idéntico al de la app del Módulo 1, excepto por una línea nueva en la parte inferior que muestra qué visitas se borrarán:

4aa8a2cb5f527079.png

Felicitaciones por completar el codelab. El código ahora debería coincidir con el contenido de la carpeta del repositorio del módulo 7. Ahora está todo listo para migrar a Cloud Tasks en el módulo 8.

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, lo que agregó compatibilidad para hacer un seguimiento de los visitantes y generó la app de muestra del módulo 7. La siguiente migración te enseña a actualizar de las tareas de envío de App Engine a Cloud Tasks si decides hacerlo. Desde el otoño de 2021, los usuarios ya no tienen que migrar a Cloud Tasks cuando actualizan a Python 3. Obtén más información sobre este tema en la siguiente sección.

Si quieres migrar a Cloud Tasks, el siguiente es el codelab del módulo 8. Además de eso, hay otras migraciones que debes tener en cuenta, como Cloud Datastore, Cloud Memorystore, Cloud Storage o Cloud Pub/Sub (colas de extracción). 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 agrupados en paquetes a los entornos de ejecución de segunda generación (originalmente disponibles solo en los entornos de ejecución de primera generación), lo que significa que ya no es necesario migrar de servicios agrupados en paquetes, como la lista de tareas en cola de App Engine, a equivalentes independientes de Cloud o de terceros, como Cloud Tasks, cuando transfieres 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 7, a continuación se vinculan las versiones de Python 3 de las apps de los módulos 1 y 7 que se transfirieron a Python 3 y que siguen usando App Engine NDB y Task Queue.

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 2 (INICIAR) y el módulo 7 (FINALIZAR).

Codelab

Python 2

Python 3

Módulo 1

código

code (no se incluye en este instructivo)

Módulo 7 (este codelab)

código

code (no se incluye en este instructivo)

Recursos en línea

A continuación, se incluyen recursos en línea que pueden ser pertinentes para este instructivo:

Lista de tareas en cola de App Engine

Plataforma de App Engine

Otra información de la nube

Videos

Licencia

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