Migra desde las tareas de extracción de la lista de tareas en cola de App Engine a Cloud Pub/Sub (módulo 19)

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.

El objetivo de este codelab es mostrar a los desarrolladores de App Engine con Python 2 cómo migrar de las tareas de extracción de App Engine Task Queue a Cloud Pub/Sub. También hay una migración implícita de App Engine NDB a Cloud NDB para el acceso a Datastore (que se aborda principalmente en el módulo 2), así como una actualización a Python 3.

En el módulo 18, aprenderás a agregar el uso de tareas de extracción en tu app. En este módulo, tomarás la app terminada del módulo 18 y migrarás ese uso a Cloud Pub/Sub. 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,

Requisitos

Encuesta

¿Cómo usarás este instructivo?

Solo lo leeré Lo 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, 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.

Los módulos de migración del 7 al 9 abarcan la migración de tareas de envío, mientras que los módulos del 18 al 19 se enfocan en la migración de tareas de extracción. Si bien Cloud Tasks se adapta mejor a las tareas de envío de Task Queues, Pub/Sub no es un análogo tan cercano a las tareas de extracción de Task Queues.

Pub/Sub tiene más funciones que la funcionalidad de extracción que proporciona Task Queue. Por ejemplo, Pub/Sub también tiene la funcionalidad de envío, pero Cloud Tasks se parece más a las tareas de envío de Task Queue, por lo que el envío de Pub/Sub no se incluye en ninguno de los módulos de migración. En este codelab del módulo 19, se demuestra cómo cambiar el mecanismo de encolamiento de las colas de extracción de Task Queue a Pub/Sub, así como migrar de App Engine NDB a Cloud NDB para el acceso a Datastore, repitiendo la migración del módulo 2.

Si bien el código del módulo 18 se "anuncia" como una app de ejemplo de Python 2, la fuente en sí es compatible con Python 2 y 3, y sigue siendo así incluso después de migrar a Cloud Pub/Sub (y Cloud NDB) aquí en el 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
  4. Habilita las nuevas APIs o los nuevos servicios de Google Cloud

Estos pasos garantizan que comiences con un código funcional y que esté listo para la migración a los servicios de Cloud.

1. Configura el proyecto

Si completaste el codelab del módulo 18, 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 durante este codelab. Úsalo cada vez que encuentres la variable PROJECT_ID.

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

Uno de los requisitos previos es una app de App Engine del módulo 18 que funcione, por lo que debes completar su codelab (recomendado; vínculo anterior) o copiar el código del módulo 18 del repositorio. Sin importar si usas el tuyo o el nuestro, aquí es donde comenzaremos ("START"). Este codelab te guía por la migración y concluye con un código que se parece al que se encuentra en la carpeta del repositorio del módulo 19 (“FINISH”).

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

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

3. (Vuelve a) implementar y validar la app de referencia

Sigue estos pasos para implementar la app del módulo 18:

  1. Borra la carpeta lib si existe y ejecuta pip install -t lib -r requirements.txt para volver a completar lib. Es posible que debas usar pip2 si tienes instalados Python 2 y 3 en tu máquina de desarrollo.
  2. Asegúrate de haber instalado y inicializado la herramienta de línea de comandos de gcloud, y de haber revisado su uso.
  3. (Opcional) Configura tu proyecto de Cloud con gcloud config set project PROJECT_ID si no quieres ingresar PROJECT_ID con cada comando gcloud que emitas.
  4. Implementa la app de ejemplo con gcloud app deploy
  5. Confirma que la app se ejecuta según lo esperado y sin problemas. Si completaste el codelab del módulo 18, la app mostrará los visitantes principales junto con las visitas más recientes (como se ilustra a continuación). De lo contrario, es posible que no haya recuentos de visitantes para mostrar.

b667551dcbab1a09.png

Antes de migrar la app de ejemplo del módulo 18, primero debes habilitar los servicios de Cloud que usará la app modificada.

4. Habilita las nuevas APIs o los nuevos servicios de Google Cloud

La app anterior usaba los servicios agrupados en paquetes de App Engine, que no requieren configuración adicional, pero los servicios independientes de Cloud sí, y la app actualizada empleará tanto Cloud Pub/Sub como Cloud Datastore (a través de la biblioteca cliente de Cloud NDB). App Engine y ambas APIs de Cloud tienen cuotas del nivel "Siempre gratis", por lo que, siempre que te mantengas dentro de esos límites, no deberías incurrir en cargos por completar este instructivo. Las APIs de Cloud se pueden habilitar desde la consola de Cloud o desde la línea de comandos, según tus preferencias.

Desde la consola de Cloud

Ve a la página de la biblioteca del Administrador de APIs (para el proyecto correcto) en la consola de Cloud y busca las APIs de Cloud Datastore y Cloud Pub/Sub con la barra de búsqueda que se encuentra en el centro de la página:

c7a740304e9d35b.png

Haz clic en el botón Habilitar para cada API por separado. Es posible que se te solicite información de facturación. Por ejemplo, esta es la página de la biblioteca de la API de Cloud Pub/Sub:

1b6c0a2a73124f6b.jpeg

Desde la línea de comandos

Si bien habilitar APIs desde la consola es visualmente informativo, algunas personas prefieren la línea de comandos. Ejecuta el comando gcloud services enable pubsub.googleapis.com datastore.googleapis.com para habilitar ambas APIs al mismo tiempo:

$ gcloud services enable pubsub.googleapis.com datastore.googleapis.com
Operation "operations/acat.p2-aaa-bbb-ccc-ddd-eee-ffffff" finished successfully.

Es posible que se te solicite información de facturación. Si deseas habilitar otras APIs de Cloud y saber cuáles son sus URIs, puedes encontrarlos en la parte inferior de la página de la biblioteca de cada API. Por ejemplo, observa pubsub.googleapis.com como el "Nombre del servicio" en la parte inferior de la página de Pub/Sub que se muestra arriba.

Una vez que completes los pasos, tu proyecto podrá acceder a las APIs. Ahora es el momento de actualizar la aplicación para usar esas APIs.

4. Crea recursos de Pub/Sub

A continuación, se resume el orden de la secuencia del flujo de trabajo de Task Queue del módulo 18:

  1. En el módulo 18, se usó el archivo queue.yaml para crear una cola de extracción llamada pullq.
  2. La app agrega tareas a la cola de extracción para hacer un seguimiento de los visitantes.
  3. Un trabajador procesa las tareas de forma eventual, y se arriendan por un período finito (una hora).
  4. Las tareas se ejecutan para registrar los recuentos de visitantes recientes.
  5. Las tareas se borran de la cola cuando se completan.

Replicarás un flujo de trabajo similar con Pub/Sub. En la siguiente sección, se presenta la terminología básica de Pub/Sub y tres formas diferentes de crear los recursos necesarios de Pub/Sub.

Terminología de la lista de tareas en cola de App Engine (extracción) en comparación con Cloud Pub/Sub

Cambiar a Pub/Sub requiere un pequeño ajuste en tu vocabulario. A continuación, se indican las categorías principales junto con los términos relevantes de ambos productos. También puedes revisar la guía de migración, que incluye comparaciones similares.

  • Estructura de datos de la cola: Con Task Queue, los datos se envían a colas de extracción; con Pub/Sub, los datos se envían a temas.
  • Unidades de datos en cola: Las tareas de extracción con Task Queue se denominan mensajes con Pub/Sub.
  • Procesadores de datos: Con Task Queue, los trabajadores acceden a las tareas de extracción; con Pub/Sub, necesitas suscripciones o suscriptores para recibir mensajes.
  • Extracción de datos: Alquilar una tarea de extracción es lo mismo que extraer un mensaje de un tema (a través de una suscripción).
  • Limpieza/finalización: Borrar una tarea de Task Queues de una cola de extracción cuando terminas es análogo a confirmar un mensaje de Pub/Sub.

Aunque el producto de filas cambia, el flujo de trabajo sigue siendo relativamente similar:

  1. En lugar de una fila de extracción, la app usa un tema llamado pullq.
  2. En lugar de agregar tareas a una cola de extracción, la app envía mensajes a un tema (pullq).
  3. En lugar de que un trabajador tome tareas en alquiler de la cola de extracción, un suscriptor llamado worker extrae mensajes del tema pullq.
  4. La app procesa las cargas útiles de los mensajes y aumenta los recuentos de visitantes en Datastore.
  5. En lugar de borrar tareas de la cola de extracción, la app confirma los mensajes procesados.

Con Task Queue, la configuración implica la creación de la lista de extracción. Con Pub/Sub, la configuración requiere la creación de un tema y una suscripción. En el módulo 18, procesamos queue.yaml fuera de la ejecución de la app. Ahora, se debe hacer lo mismo con Pub/Sub.

Existen tres opciones para crear temas y suscripciones:

  1. En la consola de Cloud
  2. Desde la línea de comandos
  3. Desde el código (secuencia de comandos corta de Python)

Elige una de las siguientes opciones y sigue las instrucciones correspondientes para crear tus recursos de Pub/Sub.

En la consola de Cloud

Para crear un tema desde Cloud Console, sigue estos pasos:

  1. Ve a la página Temas de Pub/Sub de la consola de Cloud.
  2. Haz clic en Crear tema en la parte superior. Se abrirá una nueva ventana de diálogo (consulta la imagen a continuación).
  3. En el campo ID del tema, ingresa pullq.
  4. Anula la selección de todas las opciones marcadas y selecciona Clave de encriptación administrada por Google.
  5. Haz clic en el botón Crear tema.

Así se ve el diálogo de creación de temas:

a05cfdbf64571ceb.png

Ahora que tienes un tema, debes crear una suscripción para ese tema:

  1. Ve a la página Suscripciones a Pub/Sub de la consola de Cloud.
  2. Haz clic en Crear suscripción en la parte superior (consulta la imagen a continuación).
  3. Ingresa worker en el campo ID de suscripción.
  4. Elige pullq en el menú desplegable Selecciona un tema de Cloud Pub/Sub y anota su "nombre de ruta de acceso completo", por ejemplo, projects/PROJECT_ID/topics/pullq.
  5. En Tipo de entrega, selecciona Extracción.
  6. Deja todas las demás opciones como están y haz clic en el botón Crear.

Así se ve la pantalla de creación de suscripciones:

c5444375c20b0618.jpeg

También puedes crear una suscripción desde la página Temas. Este "acceso directo" puede ser útil para asociar temas con suscripciones. Para obtener más información sobre cómo crear suscripciones, consulta la documentación.

Desde la línea de comandos

Los usuarios de Pub/Sub pueden crear temas y suscripciones con los comandos gcloud pubsub topics create TOPIC_ID y gcloud pubsub subscriptions create SUBSCRIPTION_ID --topic=TOPIC_ID, respectivamente. La ejecución de estos comandos con un TOPIC_ID de pullq y un SUBSCRIPTION_ID de worker genera el siguiente resultado para el proyecto PROJECT_ID:

$ gcloud pubsub topics create pullq
Created topic [projects/PROJECT_ID/topics/pullq].

$ gcloud pubsub subscriptions create worker --topic=pullq
Created subscription [projects/PROJECT_ID/subscriptions/worker].

Consulta también esta página en la documentación de la guía de inicio rápido. Usar la línea de comandos puede simplificar los flujos de trabajo en los que se crean temas y suscripciones de forma periódica, y estos comandos se pueden usar en secuencias de comandos de shell para este propósito.

Desde el código (secuencia de comandos corta de Python)

Otra forma de automatizar la creación de temas y suscripciones es usar la API de Pub/Sub en el código fuente. A continuación, se muestra el código del script maker.py en la carpeta del repositorio del módulo 19.

from __future__ import print_function
import google.auth
from google.api_core import exceptions
from google.cloud import pubsub

_, PROJECT_ID = google.auth.default()
TOPIC = 'pullq'
SBSCR = 'worker'
ppc_client = pubsub.PublisherClient()
psc_client = pubsub.SubscriberClient()
TOP_PATH = ppc_client.topic_path(PROJECT_ID, TOPIC)
SUB_PATH = psc_client.subscription_path(PROJECT_ID, SBSCR)

def make_top():
    try:
        top = ppc_client.create_topic(name=TOP_PATH)
        print('Created topic %r (%s)' % (TOPIC, top.name))
    except exceptions.AlreadyExists:
        print('Topic %r already exists at %r' % (TOPIC, TOP_PATH))

def make_sub():
    try:
        sub = psc_client.create_subscription(name=SUB_PATH, topic=TOP_PATH)
        print('Subscription created %r (%s)' % (SBSCR, sub.name))
    except exceptions.AlreadyExists:
        print('Subscription %r already exists at %r' % (SBSCR, SUB_PATH))
    try:
        psc_client.close()
    except AttributeError:  # special Py2 handler for grpcio<1.12.0
        pass

make_top()
make_sub()

La ejecución de este script genera el resultado esperado (si no hay errores):

$ python3 maker.py
Created topic 'pullq' (projects/PROJECT_ID/topics/pullq)
Subscription created 'worker' (projects/PROJECT_ID/subscriptions/worker)

Llamar a la API para crear recursos que ya existen genera una excepción google.api_core.exceptions.AlreadyExists que arroja la biblioteca cliente y que el script controla correctamente:

$ python3 maker.py
Topic 'pullq' already exists at 'projects/PROJECT_ID/topics/pullq'
Subscription 'worker' already exists at 'projects/PROJECT_ID/subscriptions/worker'

Si no conoces Pub/Sub, consulta el informe técnico sobre la arquitectura de Pub/Sub para obtener más información.

5. Actualizar configuración

Las actualizaciones en la configuración incluyen tanto el cambio de varios archivos de configuración como la creación del equivalente de las colas de extracción de App Engine, pero dentro del ecosistema de Cloud Pub/Sub.

Borra queue.yaml

Dejaremos de usar Task Queue por completo, así que borra queue.yaml porque Pub/Sub no usa este archivo. En lugar de crear una cola de extracción, crearás un tema de Pub/Sub (y una suscripción).

requirements.txt

Agrega google-cloud-ndb y google-cloud-pubsub a requirements.txt para unir flask del módulo 18. Tu requirements.txt actualizado del módulo 19 ahora debería verse de la siguiente manera:

flask
google-cloud-ndb
google-cloud-pubsub

Este archivo requirements.txt no incluye números de versión, lo que significa que se seleccionan las versiones más recientes. Si surgen incompatibilidades, sigue la práctica estándar de usar números de versión para fijar las versiones de trabajo de una app.

app.yaml

Los cambios en app.yaml varían según si te quedas con Python 2 o actualizas a Python 3.

Python 2

La actualización anterior de requirements.txt agrega el uso de las bibliotecas cliente de Google Cloud. Estos requieren asistencia adicional de App Engine, es decir, un par de bibliotecas integradas, setuptools y grpcio. El uso de bibliotecas integradas requiere una sección libraries en app.yaml y números de versión de la biblioteca, o "latest" para la versión más reciente disponible en los servidores de App Engine. El módulo 18 app.yaml aún no tiene una de esas secciones:

ANTES:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

Agrega una sección libraries a app.yaml junto con entradas para setuptools y grpcio, y selecciona sus versiones más recientes. También agrega una entrada de marcador de posición runtime para Python 3, comentada junto con una versión actual de 3.x, por ejemplo, 3.10, en el momento de escribir este documento. Con estos cambios, app.yaml ahora se ve de la siguiente manera:

DESPUÉS:

#runtime: python310
runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

libraries:
- name: setuptools
  version: latest
- name: grpcio
  version: latest

Python 3

Para los usuarios de Python 3 y app.yaml, se trata de quitar elementos. En esta sección, borrarás la sección handlers, las directivas threadsafe y api_version, y no crearás una sección libraries.

Los entornos de ejecución de segunda generación no proporcionan bibliotecas integradas de terceros, por lo que no se necesita una sección libraries en app.yaml. Además, ya no es necesario copiar (a veces conocido como vendoring o self-bundling) paquetes de terceros no integrados. Solo debes enumerar las bibliotecas de terceros que usa tu app en requirements.txt.

La sección handlers en app.yaml se usa para especificar controladores de archivos estáticos y de aplicaciones (secuencias de comandos). Dado que el entorno de ejecución de Python 3 requiere que los frameworks web realicen su propio enrutamiento, todos los controladores de secuencia de comandos deben cambiarse a auto. Si tu app (como la del módulo 18) no entrega archivos estáticos, todas las rutas serían auto, lo que las haría irrelevantes. Como resultado, la sección handlers tampoco es necesaria, así que bórrala.

Por último, las directivas threadsafe y api_version no se usan en Python 3, por lo que también debes borrarlas. En resumen, debes borrar todas las secciones de app.yaml para que solo quede la directiva runtime, que especifica una versión moderna de Python 3, por ejemplo, 3.10. Así se ve app.yaml antes y después de estas actualizaciones:

ANTES:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

DESPUÉS:

runtime: python310

Para quienes no están listos para borrar todo de su app.yaml para Python 3, proporcionamos un archivo alternativo app3.yaml en la carpeta del repo del módulo 19. Si deseas usarlo para las implementaciones, asegúrate de agregar este nombre de archivo al final del comando: gcloud app deploy app3.yaml (de lo contrario, se usará de forma predeterminada y se implementará tu app con el archivo app.yaml de Python 2 que dejaste sin cambios).

appengine_config.py

Si actualizas a Python 3, no necesitas appengine_config.py, así que bórralo. El motivo por el que no es necesario es que la compatibilidad con bibliotecas de terceros solo requiere especificarlas en requirements.txt. Si eres usuario de Python 2, sigue leyendo.

El appengine_config.py del módulo 18 tiene el código adecuado para admitir bibliotecas de terceros, por ejemplo, Flask y las bibliotecas cliente de Cloud que se acaban de agregar a requirements.txt:

ANTES:

from google.appengine.ext import vendor

# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)

Sin embargo, este código por sí solo no es suficiente para admitir las bibliotecas integradas que acabamos de agregar (setuptools, grpcio). Se necesitan algunas líneas más, por lo que debes actualizar appengine_config.py para que se vea de la siguiente manera:

DESPUÉS:

import pkg_resources
from google.appengine.ext import vendor

# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)
# Add libraries to pkg_resources working set to find the distribution.
pkg_resources.working_set.add_entry(PATH)

En la documentación sobre la migración de servicios agrupados en paquetes, encontrarás más detalles sobre los cambios necesarios para admitir las bibliotecas cliente de Cloud.

Otras actualizaciones de configuración

Si tienes una carpeta lib, bórrala. Si eres usuario de Python 2, vuelve a llenar la carpeta lib con el siguiente comando:

pip install -t lib -r requirements.txt  # or pip2

Si tienes instalados Python 2 y 3 en tu sistema de desarrollo, es posible que debas usar pip2 en lugar de pip.

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

En esta sección, se incluyen actualizaciones del archivo principal de la aplicación, main.py, que reemplazan el uso de las colas de extracción de App Engine Task Queue por Cloud Pub/Sub. No hay cambios en la plantilla web, templates/index.html. Ambas apps deberían funcionar de forma idéntica y mostrar los mismos datos.

Actualizar las importaciones y la inicialización

Se realizaron varias actualizaciones en las importaciones y la inicialización:

  1. En el caso de las importaciones, reemplaza App Engine NDB y Task Queue por Cloud NDB y Pub/Sub.
  2. Se cambió el nombre de pullq de un nombre de QUEUE a un nombre de TOPIC.
  3. Con las tareas de extracción, el trabajador las alquilaba por una hora, pero con Pub/Sub, los tiempos de espera se miden por mensaje, por lo que debes borrar la constante HOUR.
  4. Las APIs de Cloud requieren el uso de un cliente de API, por lo que debes iniciar los de Cloud NDB y Cloud Pub/Sub, y este último proporciona clientes para temas y suscripciones.
  5. Pub/Sub requiere el ID del proyecto de Cloud, por lo que debes importarlo y obtenerlo de google.auth.default().
  6. Pub/Sub requiere "nombres de ruta de acceso completamente calificados" para los temas y las suscripciones, por lo que debes crearlos con las funciones de conveniencia *_path().

A continuación, se muestran las importaciones y la inicialización del módulo 18, seguidas de cómo deberían verse las secciones después de implementar los cambios anteriores, con la mayoría del código nuevo siendo varios recursos de Pub/Sub:

ANTES:

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__)

DESPUÉS:

from flask import Flask, render_template, request
import google.auth
from google.cloud import ndb, pubsub

LIMIT = 10
TASKS = 1000
TOPIC = 'pullq'
SBSCR = 'worker'

app = Flask(__name__)
ds_client  = ndb.Client()
ppc_client = pubsub.PublisherClient()
psc_client = pubsub.SubscriberClient()
_, PROJECT_ID = google.auth.default()
TOP_PATH = ppc_client.topic_path(PROJECT_ID, TOPIC)
SUB_PATH = psc_client.subscription_path(PROJECT_ID, SBSCR)

Actualizaciones del modelo de datos de visitas

El modelo de datos de Visit no cambia. El acceso a Datastore requiere el uso explícito del administrador de contexto del cliente de la API de Cloud NDB, ds_client.context(). En el código, esto significa que debes encapsular las llamadas a Datastore en store_visit() y fetch_visits() dentro de los bloques with de Python. Esta actualización es idéntica a la que se aborda en el módulo 2.

El cambio más relevante para Pub/Sub es reemplazar la puesta en cola de una tarea de extracción de Task Queue por la publicación de un mensaje de Pub/Sub en el tema pullq. A continuación, se muestra el código antes y después de realizar estas actualizaciones:

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

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'
    with ds_client.context():
        Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()
    ppc_client.publish(TOP_PATH, remote_addr.encode('utf-8'))

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

Actualizaciones del modelo de datos de VisitorCount

El modelo de datos VisitorCount no cambia y hace fetch_counts(), excepto por encapsular su consulta de Datastore dentro de un bloque with, como se ilustra a continuación:

ANTES:

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

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

DESPUÉS:

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

def fetch_counts(limit):
    'get top visitors'
    with ds_client.context():
        return VisitorCount.query().order(-VisitorCount.counter).fetch(limit)

Actualiza el código del trabajador

El código del trabajador se actualiza en lo que respecta al reemplazo de NDB por Cloud NDB y de Task Queue por Pub/Sub, pero su flujo de trabajo sigue siendo el mismo.

  1. Encapsula las llamadas a Datastore en el bloque with del administrador de contexto de Cloud NDB.
  2. La limpieza de Task Queue implica borrar todas las tareas de la cola de extracción. Con Pub/Sub, los "IDs de confirmación de recepción" se recopilan en acks y, luego, se borran o se confirman al final.
  3. Las tareas de extracción de la lista de tareas en cola se arriendan de manera similar a la extracción de los mensajes de Pub/Sub. Si bien las tareas de extracción se borran con los objetos de tareas mismos, los mensajes de Pub/Sub se borran a través de sus IDs de confirmación.
  4. Las cargas útiles de los mensajes de Pub/Sub requieren bytes (no cadenas de Python), por lo que hay cierta codificación y decodificación UTF-8 cuando se publican y se extraen mensajes de un tema, respectivamente.

Reemplaza log_visitors() por el siguiente código actualizado que implementa los cambios que acabamos de describir:

ANTES:

@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))

DESPUÉS:

@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 = {}
    acks = set()
    rsp = psc_client.pull(subscription=SUB_PATH, max_messages=TASKS)
    msgs = rsp.received_messages
    for rcvd_msg in msgs:
        acks.add(rcvd_msg.ack_id)
        visitor = rcvd_msg.message.data.decode('utf-8')
        tallies[visitor] = tallies.get(visitor, 0) + 1
    if acks:
        psc_client.acknowledge(subscription=SUB_PATH, ack_ids=acks)
    try:
        psc_client.close()
    except AttributeError:  # special handler for grpcio<1.12.0
        pass

    # increment those counts in Datastore and return
    if tallies:
        with ds_client.context():
            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(msgs), len(tallies))

No hay cambios en el controlador principal de la aplicación root(). Tampoco se necesitan cambios en el archivo de plantilla HTML, templates/index.html, por lo que esto abarca todas las actualizaciones necesarias. ¡Felicitaciones por llegar a tu nueva aplicación del módulo 19 con Cloud Pub/Sub!

7. Resumen/Limpieza

Implementa tu app para verificar que funcione según lo previsto y en cualquier resultado reflejado. También ejecuta el trabajador 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 haber creado el tema pullq y la suscripción worker. 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 18, excepto que reemplazaste correctamente todo el mecanismo de encolamiento subyacente:

b667551dcbab1a09.png

El frontend web de la app ahora verifica que esta parte de la aplicación funcione. Si bien esta parte de la app consulta y muestra correctamente los visitantes principales y las visitas más recientes, recuerda que la app registra esta visita y crea una tarea de extracción para agregar este visitante al recuento general. Esa tarea ahora está en la cola y espera su procesamiento.

Puedes ejecutar esto con un servicio de backend de App Engine, un trabajo de cron, navegar a /log o emitir una solicitud HTTP de línea de comandos. A continuación, se muestra un ejemplo de ejecución y salida de la llamada al código del trabajador con curl (sustituye 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.

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:

  • Los diferentes componentes de Cloud Pub/Sub tienen un nivel gratuito. Determina tu uso general para tener una mejor idea de las implicaciones de los costos y consulta su página de precios para obtener más detalles.
  • El servicio App Engine Datastore lo proporciona Cloud Datastore (Cloud Firestore en modo Datastore), que también tiene un nivel gratuito. Consulta su página de precios para obtener más información.

Próximos pasos

Además de este instructivo, otros módulos de migración que se enfocan en dejar de usar los servicios agrupados heredados que debes tener en cuenta son los siguientes:

  • Módulo 2: Migra de ndb de App Engine a Cloud NDB
  • Módulos 7 a 9: Migra de la lista de tareas en cola de App Engine (tareas de envío) a Cloud Tasks
  • Módulos 12 y 13: Migra de Memcache de App Engine a Cloud Memorystore
  • Módulos 15 y 16: Migra de Blobstore de App Engine a Cloud Storage

App Engine ya no es la única plataforma sin servidores de Google Cloud. Si tienes una app de App Engine pequeña o una que tiene funcionalidad limitada y deseas convertirla en un microservicio independiente, o bien quieres dividir una app monolítica en varios componentes reutilizables, estos son buenos motivos para considerar la posibilidad de migrar a Cloud Functions. Si la contenerización se convirtió en parte de tu flujo de trabajo de desarrollo de aplicaciones, en especial si consta de una canalización de CI/CD (integración continua/entrega o implementación continua), considera migrar a Cloud Run. Estos casos se abordan en los siguientes módulos:

  • Migra de App Engine a Cloud Functions: Consulta el módulo 11
  • Migra de App Engine a Cloud Run: Consulta el módulo 4 para organizar tu app en contenedores con Docker o el módulo 5 para hacerlo sin contenedores, conocimientos de Docker ni Dockerfiles.

Cambiar a otra plataforma sin servidores es opcional, y te recomendamos que consideres las mejores opciones para tus apps y casos de uso antes de realizar cualquier cambio.

Independientemente del módulo de migración que consideres a continuación, se puede acceder a todo el contenido de Serverless Migration Station (codelabs, videos, código fuente [cuando esté disponible]) en su repositorio de código abierto. El README del repo también proporciona orientación sobre qué migraciones considerar y cualquier "orden" relevante de los módulos de migración.

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

Codelab

Python 2

Python 3

Módulo 18

código

(n/a)

Módulo 19 (este codelab)

código

(igual que Python 2, excepto que se usa app3.yaml, a menos que hayas actualizado app.yaml como se explicó anteriormente)

Referencias en línea

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

Lista de tareas en cola de App Engine

Cloud Pub/Sub

App Engine NDB y Cloud NDB (Datastore)

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.