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

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.

El propósito de este codelab es mostrarles a los desarrolladores de App Engine de Python 2 cómo migrar desde las tareas de extracción de la lista de tareas en cola de App Engine 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 a tu app. En este módulo, tomarás la app finalizada del Módulo 18 y migrarás ese uso a Cloud Pub/Sub. Aquellos que usen listas de tareas en cola para tareas push migrarán a Cloud Tasks en su lugar, por lo que deberán consultar los módulos 7-9.

En un próximo lab,

Requisitos

Encuesta

¿Cómo usarás este instructivo?

Solo leerlo Léelo y completa 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, 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.

Los módulos 7 a 9 de migración abordan la migración de tareas push, mientras que los módulos 18 a 19 se enfocan en la migración de tareas de extracción. Si bien Cloud Tasks coincide más con las tareas push de la lista de tareas en cola, Pub/Sub no es tan parecido a las tareas de extracción de la lista de tareas en cola.

Pub/Sub tiene más funciones que la funcionalidad de extracción que proporciona la lista de tareas en cola. Por ejemplo, Pub/Sub también tiene la funcionalidad push, sin embargo, Cloud Tasks se parece más a las tareas push de la lista de tareas en cola, por lo que Pub/Sub no está cubierto por ninguno de los módulos de migración. En este codelab del módulo 19, se muestra cómo cambiar el mecanismo de cola de las listas de extracción de la lista de tareas en cola a Pub/Sub, así como migrar de App Engine NDB a Cloud NDB para el acceso a Datastore, lo que repite la migración del Módulo 2.

Mientras que el código del módulo 18 es como app de muestra de Python 2, la fuente en sí es compatible con Python 2 y 3, y se mantiene así incluso después de migrar a Cloud Pub/Sub (y Cloud NDB) en el módulo 19.

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
  4. Habilitar nuevas APIs o servicios de Google Cloud

Estos pasos garantizan que comiences con el código de trabajo y que esté listo para migrar a los servicios de Cloud.

1. Configura el proyecto

Si completaste el codelab del módulo 18, vuelve a usar ese mismo proyecto (y código). Como alternativa, crea un proyecto completamente nuevo o reutiliza otro proyecto existente. Asegúrate de que el proyecto tenga una cuenta de facturación activa y una app de App Engine habilitada. Encuentra tu ID del proyecto, ya que necesitarás tenerlo a mano durante este codelab, utilízalo cada vez que encuentres la variable PROJECT_ID.

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

Uno de los requisitos previos es contar con una app de App Engine del Módulo 18 que funcione, así que completa el codelab (se recomienda incluir el vínculo anterior) o copia el código del módulo 18 del repositorio. Ya sea que use el suyo o el nuestro, aquí es por donde comenzaremos ("COMENZAR"). En este codelab, se explica la migración y se concluye con un código similar al que hay 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 de la siguiente manera, posiblemente con una carpeta lib:

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

3. (Re)Implementa y valida la app de referencia

Ejecuta los siguientes pasos para implementar la app del módulo 18:

  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 pip2 si tienes Python 2 y 3 instalados en tu máquina de desarrollo.
  2. Asegúrate de haber instalado e 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 el PROJECT_ID con cada comando gcloud que emitas.
  4. Implementa la app de ejemplo con gcloud app deploy
  5. Confirma que la app se ejecute como se espera sin problemas. Si completaste el codelab del módulo 18, la app mostrará los visitantes principales junto con las visitas más recientes (que se ilustran a continuación). De lo contrario, es posible que no se muestren recuentos de visitantes.

b667551dcbab1a09.png

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

4. Habilitar nuevas APIs o servicios de Google Cloud

La app antigua usaba servicios en paquetes de App Engine que no requieren una configuración adicional, pero los servicios de Cloud independientes sí lo hacen, y la app actualizada empleará Cloud Pub/Sub y Cloud Datastore (a través de la biblioteca cliente de Cloud NDB). App Engine y ambas APIs de Cloud tienen la opción “Siempre gratuito” nivel y, siempre que te mantengas por debajo de esos límites, no se generarán cargos si completas 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 Biblioteca del Administrador de API (del 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 aparece en el medio de la página:

c7a740304e9d35b.png

Haz clic en el botón Habilitar para cada API por separado; es posible que se te soliciten datos 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 es visualmente informativo habilitar APIs desde la consola, algunos 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 le soliciten datos de facturación. Si quieres habilitar otras APIs de Cloud y conocer sus URI, puedes encontrarlas 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, 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

Resumen del orden de secuencias del flujo de trabajo de la lista de tareas en cola del Módulo 18:

  1. En el módulo 18, se usó el archivo queue.yaml para crear una lista de extracción llamada pullq.
  2. La app agrega tareas a la lista de extracción para hacer un seguimiento de los visitantes.
  3. Un trabajador procesa las tareas y las asigna durante un tiempo limitado (una hora).
  4. Se ejecutan tareas para contar los recuentos recientes de visitantes.
  5. Las tareas se borran de la cola cuando se completan.

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

Comparación entre la terminología de lista de tareas en cola (extracción) de App Engine y Cloud Pub/Sub

Para cambiar a Pub/Sub, debes ajustar levemente tu vocabulario. A continuación, se indican las categorías principales junto con los términos relevantes de ambos productos. Además, revisa la guía de migración, que incluye comparaciones similares.

  • Estructura de datos en cola: con la lista de tareas en cola, los datos pasan a listas de extracción. con Pub/Sub, los datos se dividen en temas.
  • Unidades de datos en cola: las tareas de extracción con la lista de tareas en cola se denominan mensajes con Pub/Sub.
  • Procesadores de datos: Con la lista de tareas en cola, los trabajadores acceden a las tareas de extracción. con Pub/Sub, necesitas suscripciones/suscriptores para recibir mensajes
  • Extracción de datos: Dejar una tarea de extracción es lo mismo que extraer un mensaje de un tema (a través de una suscripción).
  • Limpieza y finalización: Borrar una tarea de la lista de tareas en cola de una lista de extracción al terminar es similar a confirmar un mensaje de Pub/Sub.

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

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

Con la lista de tareas en cola, la configuración implica crear una 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. lo mismo debe hacerse con Pub/Sub.

Existen tres opciones para crear temas y suscripciones:

  1. Desde 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 opciones que aparecen a continuación y sigue las instrucciones correspondientes para crear tus recursos de Pub/Sub.

Desde la consola de Cloud

Para crear un tema desde la consola de Cloud, sigue estos pasos:

  1. Ve a la página Temas de Pub/Sub de Cloud Console.
  2. Haz clic en Crear tema en la parte superior. se abre un nuevo cuadro 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 elige 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 él:

  1. Ve a la página Suscripciones de Pub/Sub de la consola de Cloud.
  2. Haz clic en Crear suscripción en la parte superior (consulta la siguiente imagen).
  3. Ingresa worker en el campo ID de la suscripción.
  4. Elige pullq en el menú desplegable Seleccionar un tema de Cloud Pub/Sub y presta atención a su "nombre de ruta completamente calificado". 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 la suscripción:

c5444375c20b0618.jpeg

También puede crear una suscripción desde la página Temas, este "acceso directo" puede resultarte útil para ayudar a 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. Si se ejecutan con una TOPIC_ID de pullq y una SUBSCRIPTION_ID de worker, se obtiene 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 podría simplificar los flujos de trabajo en los que los temas y las suscripciones se crean de forma regular, y esos comandos se pueden usar en las 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. El siguiente es el código de la secuencia de comandos 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 esta secuencia de comandos da como resultado el resultado esperado (siempre y cuando no haya 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 existentes genera una excepción google.api_core.exceptions.AlreadyExists que arroja la biblioteca cliente y que la secuencia de comandos controla de forma correcta:

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

Si es la primera vez que usas Pub/Sub, consulta el informe sobre la arquitectura de Pub/Sub para obtener más información.

5. Actualizar configuración

Las actualizaciones de la configuración incluyen el cambio de varios archivos de configuración y la creación del equivalente de listas de extracción de App Engine, pero dentro del ecosistema de Cloud Pub/Sub.

Borra queue.yaml

Estamos saliendo de la lista de tareas en cola por completo, por lo que debes borrar queue.yaml porque Pub/Sub no usa este archivo. En lugar de crear una lista de extracción, deberás crear 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. La actualización requirements.txt del módulo 19 debería verse de la siguiente manera:

flask
google-cloud-ndb
google-cloud-pubsub

Este archivo requirements.txt no presenta ningún número de versión, lo que significa que se seleccionaron las versiones más recientes. Si surge alguna incompatibilidad, sigue la práctica estándar de usar números de versión para bloquear versiones de trabajo de una app.

app.yaml

Los cambios a app.yaml difieren en función de 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. Estas requieren asistencia adicional de App Engine, en particular, 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 biblioteca o “más reciente” para conocer las últimas novedades sobre 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 las versiones más recientes. Además, agrega una entrada de marcador de posición runtime para Python 3, comentada junto con una versión 3.x actual, por ejemplo, 3.10, en el momento de la redacción de este documento. Con estos cambios, app.yaml ahora tendrá el siguiente aspecto:

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 y 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 de terceros integradas, por lo que no se necesita una sección libraries en app.yaml. Además, ya no se requiere la copia de paquetes de terceros (también conocida como proveedor o autoagrupación) no integrados. Solo debes enumerar las bibliotecas de terceros que usa tu app en requirements.txt.

La sección handlers de app.yaml se usa para especificar controladores de aplicaciones (secuencia de comandos) y de archivos estáticos. Dado que el entorno de ejecución de Python 3 requiere frameworks web para realizar 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, por lo que son irrelevantes. Como resultado, la sección handlers tampoco es necesaria, así que bórrala.

Por último, en Python 3 no se usan las directivas threadsafe ni api_version, así que bórralas también. En conclusión, debes borrar todas las secciones de app.yaml para que solo quede la directiva runtime y se especifique 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 aquellos que no están listos para borrar todo de su app.yaml para Python 3, proporcionamos un archivo alternativo app3.yaml en la carpeta del repositorio del módulo 19. Si deseas usarla en su lugar para las implementaciones, asegúrate de agregar este nombre de archivo al final de tu comando: gcloud app deploy app3.yaml (de lo contrario, se implementará la app de forma predeterminada con el archivo app.yaml de Python 2 que no modificaste).

appengine_config.py

Si actualizas a Python 3, no es necesario usar appengine_config.py, así que bórralo. Esto no es necesario porque la compatibilidad con bibliotecas de terceros solo requiere que se especifiquen en requirements.txt. usuarios de Python 2, continúa leyendo.

El appengine_config.py del módulo 18 tiene el código apropiado 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 se acaban de agregar (setuptools, grpcio). Necesitan algunas líneas más, así que actualiza 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)

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

Otras actualizaciones de configuración

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

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

Si tienes Python 2 y 3 instalados 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 presentan actualizaciones del archivo principal de la aplicación, main.py, y se reemplaza el uso de las listas de extracción de la lista de tareas en cola de App Engine con Cloud Pub/Sub. No hay cambios en la plantilla web, templates/index.html. Ambas apps deben funcionar de forma idéntica y mostrar los mismos datos.

Actualizar las importaciones y la inicialización

Hay varias actualizaciones para las importaciones y la inicialización:

  1. Para las importaciones, reemplaza App Engine NDB y la lista de tareas en cola con Cloud NDB y Pub/Sub.
  2. Cambia el nombre de pullq de QUEUE a TOPIC.
  3. Con las tareas de extracción, el trabajador las asignó durante 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 iniciarlas para Cloud NDB y Cloud Pub/Sub. Esta última función proporciona clientes para los temas y las suscripciones.
  5. Pub/Sub requiere el ID del proyecto de Cloud, así que impórtalo y obtenlo de google.auth.default().
  6. Pub/Sub requiere “nombres de rutas de acceso completamente calificados” para temas y suscripciones, así que créalos usando las funciones prácticas de *_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 parte del código nuevo de 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)

Visitar las actualizaciones de modelos de datos

El modelo de datos de Visit no cambia. El acceso a Datastore requiere el uso explícito de ds_client.context(), el administrador de contexto del cliente de la API de Cloud NDB. En el código, esto significa que unes las llamadas de 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 la lista de tareas en cola con 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 realiza fetch_counts(), excepto por unir la 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, ya que reemplaza NBS por Cloud NDB y lista de tareas en cola con Pub/Sub, pero el flujo de trabajo sigue siendo el mismo.

  1. Une llamadas de Datastore en el bloque with del administrador de contexto de Cloud NDB.
  2. La limpieza de lista de tareas en cola implica borrar todas las tareas de la lista de extracción. Con Pub/Sub, “ID de confirmación de recepción” se recopilan en acks y, luego, se borran o confirman.
  3. Las tareas de extracción de la lista de tareas en cola se asignan de forma similar a como se extraen los mensajes de Pub/Sub. Si bien la eliminación de las tareas de extracción se realiza con los objetos de la tarea, los mensajes de Pub/Sub se borran a través de sus IDs de confirmación de recepción.
  4. Las cargas útiles de 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 se describen:

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 de la aplicación principal root(). Tampoco se necesitan cambios en el archivo de plantilla HTML, templates/index.html, por lo que esto une 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. Ejecuta también el trabajador para procesar los recuentos de visitantes. Después de validar la app, realiza los pasos de limpieza y considera los siguientes.

Implementa y verifica la aplicación

Asegúrate de haber creado el tema pullq y la suscripción worker. Si el proceso se completó y tu app de ejemplo está lista, implementa la app 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 cola subyacente:

b667551dcbab1a09.png

El frontend web de la app ahora verifica que esta parte de la aplicación funciona. Si bien esta parte de la app consulta y muestra con éxito los visitantes principales y las visitas más recientes, recuerda que la app registra esta visita junto con la creación de una tarea de extracción para agregar a este visitante al recuento general. La tarea ahora está en la cola a la espera de ser procesada.

Puedes ejecutarlo con un servicio de backend de App Engine, un trabajo cron, navegar a /log o emitir una solicitud HTTP de línea de comandos. A continuación, se muestra una ejecución de ejemplo y una llamada al código del trabajador por curl (sustituye tu PROJECT_ID):

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

El recuento actualizado se reflejará en la próxima visita al sitio web. Eso es todo.

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:

  • 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 la página de precios para obtener más detalles.
  • El servicio de 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

Más allá de este instructivo, otros módulos de migración que se enfocan en alejarse de los servicios en paquetes heredados que se deben considerar incluyen los siguientes:

App Engine ya no es la única plataforma sin servidores en Google Cloud. Si tienes una aplicación pequeña de App Engine o una con funcionalidad limitada y deseas convertirla en un microservicio independiente, o si deseas dividir una aplicación monolítica en varios componentes reutilizables, estas son buenas razones para considerar cambiar a Cloud Functions. Si la creación de contenedores se volvió parte del flujo de trabajo de desarrollo de tu aplicación, en especial si consta de una canalización de CI/CD (integración continua/entrega o implementación continuas), considera migrar a Cloud Run. Estas situaciones 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 alojar tu app en contenedores con Docker, o el Módulo 5 para hacerlo sin contenedores, conocimiento sobre Docker ni Dockerfile.

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, puedes 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. El README del repositorio 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 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 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, encontrarás los vínculos a las carpetas del repositorio del módulo 18 (START) y el módulo 19 (FINISH).

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 use app3.yaml, a menos que haya actualizado app.yaml como se explicó anteriormente)

Referencias en línea

A continuación, hay recursos relevantes 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 Cloud

Videos

Licencia

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