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

La serie de codelabs Serverless Migration Station (instructivos prácticos y autoaprendizaje) y los videos relacionados tienen como objetivo ayudar a los desarrolladores de Google Cloud sin servidores a modernizar sus apps guiándolos a través de una o más migraciones, en especial al alejarse 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á integrarlas con una variedad más amplia de productos de Cloud y acceder a ella, y actualizar con mayor facilidad a versiones de idiomas más recientes. Aunque inicialmente 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 cualquier otra, si corresponde.

En este codelab, aprenderás a usar las tareas push de la lista de tareas en cola 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, ya que proporcionan una breve descripción general del contenido de este instructivo.

En este módulo, agregaremos el uso de tareas push y, luego, migraremos ese uso a Cloud Tasks en el Módulo 8 y, luego, a Python 3 y Cloud Datastore en el Módulo 9. Aquellos que usen listas de tareas en cola para tareas de extracción migrarán a Cloud Pub/Sub y, en su lugar, deberán consultar los módulos 18-19.

En un próximo lab,

  • Usa la API o el servicio empaquetado de la lista de tareas en cola de App Engine
  • Agrega el uso de tareas de envío a una app básica de Flask en 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. Información general

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

La migración de tareas de extracción se aborda en los módulos 18 a 19 de migración, mientras que los módulos 7 a 9 se enfocan en la migración de tareas de envío. Para migrar desde tareas push de lista de tareas en cola de App Engine, agrega su uso a la app existente de Flask y App Engine NBS que resulta del codelab del módulo 1. En esa aplicación, una nueva vista de página registra una nueva visita y muestra las visitas más recientes del usuario. Dado que las visitas anteriores 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. Más adelante, en el módulo 8, migraremos la app de la lista de tareas en cola a Cloud Tasks.

En este instructivo, se muestran 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. (Re)Implementa y valida la app de referencia

Con estos pasos, te aseguras de comenzar a trabajar con código.

1. Configura el proyecto

Si completaste el codelab del módulo 1, te recomendamos que vuelvas a usar ese mismo proyecto (y código). Como alternativa, puedes crear un proyecto completamente nuevo o reutilizar otro proyecto existente. Asegúrate de que el proyecto tenga una cuenta de facturación activa y App Engine esté habilitado.

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

Uno de los requisitos previos de 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 repositorio. Ya sea que uses el tuyo o el nuestro, comenzaremos en el código del módulo 1. En este codelab, se explica cada paso y se concluye con un código similar 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 tener el siguiente aspecto, posiblemente con una carpeta lib:

$ 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 propagar lib. Es posible que debas usar el comando pip2 en su lugar si tienes instalados Python 2 y 3.
  2. Asegúrate de haber instalado e 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 emitido.
  4. Implementa la app de ejemplo con gcloud app deploy
  5. Confirma que la app del Módulo 1 se ejecute como se espera sin problemas y muestre 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 archivos de la aplicación

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

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

1. Actualizar importaciones

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

  • Dado que agregaremos una tarea para borrar las visitas más antiguas, la app deberá procesar las marcas de tiempo, es decir, el uso de time y datetime.
  • Para registrar información útil sobre la ejecución de la tarea, necesitamos logging.

Si agregas todas estas importaciones, verás a continuación el aspecto que tendrá tu código 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 (intercalar datos para la tarea, poner en cola una tarea nueva)

La documentación de la lista de aplicaciones en cola indica lo siguiente: "Para procesar una tarea, debes agregarla a una lista de aplicaciones en cola. 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 lo deseas, puedes agregar todas tus tareas a la cola predeterminada, sin tener que crear ni configurar otras colas”. En este codelab, se usa la cola default para abreviar. Para obtener más información sobre cómo definir tus propias listas de aplicaciones en cola, con características iguales o diferentes, consulta la documentación sobre cómo crear listas de aplicaciones en cola.

El objetivo principal de este codelab es agregar una tarea (a la lista de aplicaciones en cola default) cuyo trabajo es borrar las visitas anteriores de Datastore que ya no se muestran. La app de referencia registra cada visita (solicitud de GET a /) mediante la creación de una nueva entidad Visit y, luego, recupera y muestra las visitas más recientes. No se volverá a mostrar ni se usará ninguna de las visitas más antiguas, por lo que la tarea push borra todas las visitas anteriores a la más antigua. Para lograrlo, el comportamiento de la app debe cambiar un poco:

  1. Cuando consultes las visitas más recientes, en lugar de mostrarlas de inmediato, modifica la app para guardar la marca de tiempo de la última Visit, la más antigua 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, accesible a través de un POST HTTP a /trim. Específicamente, usa las 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 registrarla (como una cadena) y mostrar esa cadena como un valor centinela para mostrar al usuario.

Todo esto se lleva a cabo en fetch_visits(), y así se verá antes y después de realizar las 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. Agrega un controlador de tareas (código que se llama cuando se ejecuta la tarea)

Si bien la eliminación de las visitas anteriores se podría haber realizado con facilidad en fetch_visits(), ten en cuenta que esta función no tiene mucho que ver con el usuario final. Es una funcionalidad auxiliar y una buena opción para procesar de forma asíncrona fuera de las solicitudes estándar de la app. El usuario final aprovechará el beneficio de realizar consultas más rápidas, ya que habrá menos información en Datastore. Crea una nueva función trim(), llamada a través de una solicitud POST de la lista de tareas en cola a /trim, que hace lo siguiente:

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

Puedes ver todo esto en trim() a continuación. Agrégala 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. Actualizar plantilla web

Actualiza la plantilla web, templates/index.html, con este condicional 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, para que tu plantilla se vea así:

<!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 finaliza este codelab implementando la app y verificando que funcione según lo previsto y en cualquier resultado reflejado. Después de validar la app, realiza una limpieza y considera los pasos siguientes.

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. Tu código ahora debería coincidir con el que hay en la carpeta del repositorio del módulo 7. Ya está todo listo para migrar a Cloud Tasks en el módulo 8.

Limpia

General

Si ya terminaste, te recomendamos que inhabilites la aplicación de App Engine para evitar que se te facture. Sin embargo, si deseas probar o experimentar un poco más, la plataforma de App Engine tiene una cuota gratuita y, siempre y cuando no superes ese nivel de uso, no se te debería cobrar. Eso es para procesamiento, pero es posible que también se apliquen cargos por servicios relevantes de App Engine, así que consulta la página de precios para obtener más información. Si esta migración implica otros servicios de Cloud, estos se facturan por separado. En ambos casos, si corresponde, consulta la sección "Específico de este codelab" a continuación.

Para una divulgación completa, la implementación en una plataforma de procesamiento 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 ocupa parte de esa cuota. Sin embargo, es posible que vivas en una región que no cuenta con ese nivel gratuito, así que ten en cuenta el uso que haces del almacenamiento para minimizar posibles costos. “Carpetas” específicas de Cloud Storage que debes revisar incluyen

  • 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*; por ejemplo, "us" si tu app está alojada en EE.UU.

Por otro lado, si no vas a continuar con esta aplicación o algún otro codelab de migración relacionado y quieres borrar todo por completo, cierra tu proyecto.

Información específica de 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 lista de aplicaciones en cola de la lista de tareas en cola a la app de ejemplo del Módulo 1, lo que agregó compatibilidad con el seguimiento de visitantes, lo que generó la app de ejemplo del Módulo 7. En la próxima migración, aprenderás a actualizar desde las tareas de envío de App Engine a Cloud Tasks, en caso de que decidas hacerlo. A partir del otoño de 2021, los usuarios ya no tendrán que migrar a Cloud Tasks cuando actualicen a Python 3. Obtén más información sobre este tema en la siguiente sección.

Si quieres pasar a Cloud Tasks, a continuación, encontrarás el codelab del módulo 8. Además de eso, hay migraciones adicionales que se deben considerar, como Cloud Datastore, Cloud Memorystore, Cloud Storage o Cloud Pub/Sub (listas de extracción). También hay migraciones de productos cruzados a Cloud Run y Cloud Functions. Se puede acceder a todo el contenido de Serverless Migration Station (codelabs, videos, código fuente [si está disponible]) a través de su repositorio de código abierto.

7. Migración a Python 3

En otoño de 2021, el equipo de App Engine amplió la compatibilidad de muchos de los servicios en paquete a entornos de ejecución de 2a generación (que originalmente estaban disponibles solo en los entornos de ejecución de 1a generación), lo que significa que ya no es necesario migrar de servicios agrupados, como la lista de tareas en cola de App Engine, a Cloud independiente o a equivalentes de terceros, como Cloud Tasks, cuando migras tu app a Python 3. En otras palabras, puedes seguir usando la lista de tareas en cola en las aplicaciones de Python 3 de App Engine, siempre y cuando actualices el código para acceder a servicios en paquetes desde entornos de ejecución de nueva generación.

Puedes obtener más información para migrar el uso de servicios en paquetes a Python 3 en el codelab del módulo 17 y el video correspondiente. Si bien ese tema está fuera del alcance del módulo 7, en los vínculos que aparecen a continuación, encontrarás versiones de Python 3 de las apps del módulo 1 y 7 transferidas a Python 3, y aún usan App Engine NBS y Task Queue.

8. Recursos adicionales

A continuación, se incluyen recursos adicionales para los desarrolladores que exploran este módulo o uno relacionado, así como productos relacionados. Esto incluye lugares donde enviar comentarios sobre este contenido, vínculos al código y varios documentos que pueden resultarte útiles.

Problemas o comentarios sobre el codelab

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, encontrarás los vínculos a las carpetas de repositorio del Módulo 2 (COMENZAR) y 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, hay recursos en línea que pueden ser relevantes para este tutorial:

Lista de tareas en cola de App Engine

Plataforma de App Engine

Otra información de Cloud

Videos

Licencia

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