Migrar tarefas pull da fila de tarefas do App Engine para o Cloud Pub/Sub (módulo 19)

1. Visão geral

A série de codelabs da estação de migração sem servidor (tutoriais práticos e individualizados) e os vídeos relacionados têm como objetivo ajudar desenvolvedores sem servidor do Google Cloud a modernizar apps, orientando-os em uma ou mais migrações, principalmente para evitar serviços legados. Isso torna seus apps mais portáteis e oferece mais opções e flexibilidade, o que permite a integração e o acesso a uma variedade maior de produtos do Cloud e o upgrade para versões de idiomas mais recentes com mais facilidade. Embora inicialmente voltada para os primeiros usuários do Cloud, principalmente desenvolvedores do App Engine (ambiente padrão), esta série é ampla o suficiente para incluir outras plataformas sem servidor, como o Cloud Functions e o Cloud Run, ou outros lugares, se aplicável.

O objetivo deste codelab é mostrar aos desenvolvedores do App Engine em Python 2 como migrar das tarefas pull da fila de tarefas do App Engine para o Cloud Pub/Sub. Há também uma migração implícita do App Engine MapReduce para o Cloud NBS no acesso ao Datastore (abordada principalmente no Módulo 2), bem como um upgrade para o Python 3.

No módulo 18, você vai aprender a usar tarefas pull no seu app. Neste módulo, você migrará o aplicativo finalizado do Módulo 18 para o Cloud Pub/Sub. Aqueles que usam filas de tarefas para tarefas push migrarão para o Cloud Tasks e devem consultar os Módulos 7-9.

Você vai aprender a

O que é necessário

Pesquisa

Como você usará este tutorial?

Apenas ler o documento Ler e fazer os exercícios

Como você classificaria sua experiência com Python?

Iniciante Intermediário Proficiente

Como você classificaria sua experiência de uso dos serviços do Google Cloud?

Iniciante Intermediário Proficiente

2. Contexto

A Fila de Tarefas do App Engine oferece suporte a tarefas push e pull. Para melhorar a portabilidade de aplicativos, o Google Cloud recomenda a migração de serviços em pacote legados, como o Task Queue, para outros serviços independentes do Cloud ou equivalentes de terceiros.

Os Módulos de Migração 7 a 9 abordam a migração de tarefas push, enquanto os Módulos 18 a 19 tratam da migração de tarefas pull. Embora o Cloud Tasks corresponda melhor às tarefas push da fila de tarefas, o Pub/Sub não é tão parecido com as tarefas pull da fila de tarefas.

O Pub/Sub tem mais recursos do que a funcionalidade de pull fornecida pela fila de tarefas. Por exemplo, o Pub/Sub também tem a funcionalidade push. No entanto, o Cloud Tasks é mais como tarefas de push da fila de tarefas. Portanto, o push do Pub/Sub não é coberto por nenhum dos módulos de migração. Este codelab do Módulo 19 demonstra como alternar o mecanismo de enfileiramento das filas pull da fila de tarefas para o Pub/Sub, bem como como migrar do App Engine Node para o Cloud Firestore para acesso ao Datastore, repetindo a migração do Módulo 2.

Enquanto o código do Módulo 18 é "anunciado" como um app de exemplo em Python 2, a origem é compatível com Python 2 e 3 e permanece assim mesmo depois de migrar para o Cloud Pub/Sub (e o Cloud serviço) aqui no módulo 19.

Este tutorial apresenta as seguintes etapas:

  1. Configuração/Pré-trabalho
  2. Atualizar a configuração
  3. Modificar o código do aplicativo

3. Configuração/Pré-trabalho

Esta seção explica como:

  1. Configurar seu projeto do Cloud
  2. Receber app de amostra do valor de referência
  3. (Re)implantar e validar o app de referência
  4. Ativar novos serviços/APIs do Google Cloud

Essas etapas garantem que você esteja começando com o código em funcionamento e que ele esteja pronto para a migração para os serviços do Cloud.

1. Configurar projeto

Se você concluiu o codelab do módulo 18, reutilize o mesmo projeto e código. Outra opção é criar um novo projeto ou reutilizar um projeto existente. Verifique se o projeto tem uma conta de faturamento ativa e um aplicativo do App Engine ativado. Use o ID do projeto sempre que encontrar a variável PROJECT_ID. Ele precisa estar à mão durante este codelab.

2. Receber app de amostra do valor de referência

Um dos pré-requisitos é um app funcional do App Engine do Módulo 18. Portanto, conclua o codelab (recomendado, link acima) ou copie o código do módulo 18 do repositório. Não importa se você usa o seu ou o nosso, é aqui que vamos começar ("INICIAR"). Este codelab orienta você durante a migração e conclui com um código semelhante ao da pasta de repositórios do Módulo 19 ("FINISH").

Independentemente do app do Módulo 18 usado, a pasta será semelhante à mostrada abaixo, possivelmente também com uma pasta lib:

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

3. (Re)implantar e validar o app de referência

Execute as etapas abaixo para implantar o app Module 18:

  1. Exclua a pasta lib, se houver uma, e execute pip install -t lib -r requirements.txt para preencher lib novamente. Pode ser necessário usar pip2 se você tiver o Python 2 e o 3 instalados na sua máquina de desenvolvimento.
  2. Verifique se você instalou e inicializou a ferramenta de linha de comando gcloud e analisou o uso dela.
  3. (Opcional) Defina o projeto do Cloud com gcloud config set project PROJECT_ID se você não quiser inserir PROJECT_ID em cada comando gcloud emitido.
  4. Implante o app de exemplo com gcloud app deploy
  5. Confirme se o app é executado como esperado sem problemas. Se você concluiu o codelab do Módulo 18, o app vai mostrar os principais visitantes junto com as visitas mais recentes (ilustrados abaixo). Caso contrário, pode não haver contagens de visitantes a serem exibidas.

b667551dcbab1a09.png

Antes de migrar o app de exemplo do Módulo 18, ative os serviços do Cloud que o app modificado vai usar.

4. Ativar novos serviços/APIs do Google Cloud

O aplicativo antigo usava o pacote de serviços do App Engine que não requer configuração adicional, mas os serviços independentes do Cloud exigem, e o aplicativo atualizado empregará o Cloud Pub/Sub e o Cloud Datastore (por meio da biblioteca de cliente do Cloud NBS). O App Engine e as duas APIs do Cloud têm a opção "Sempre sem custo financeiro" nível. Assim, enquanto você permanecer abaixo desses limites, não haverá cobranças ao concluir este tutorial. Dependendo da sua preferência, as APIs do Cloud podem ser ativadas no console do Cloud ou na linha de comando.

No Console do Cloud

Acesse a página da biblioteca do gerenciador de APIs (para o projeto correto) no console do Cloud e procure as APIs Cloud Datastore e Cloud Pub/Sub usando a barra de pesquisa no meio da página:

c7a740304e9d35b.png

Clique no botão Ativar para cada API separadamente. Talvez você precise inserir informações de faturamento. Por exemplo, esta é a página da biblioteca de APIs do Cloud Pub/Sub:

1b6c0a2a73124f6b.jpeg

Na linha de comando,

Embora ela seja visualmente informativa para ativar APIs no console, alguns preferem a linha de comando. Emita o comando gcloud services enable pubsub.googleapis.com datastore.googleapis.com para ativar as duas APIs ao mesmo tempo:

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

Talvez você precise fornecer informações de faturamento. Para ativar outras APIs do Cloud e saber quais são os URIs, elas podem ser encontradas na parte de baixo da página da biblioteca de cada API. Por exemplo, observe pubsub.googleapis.com como o "Nome do serviço". na parte inferior da página do Pub/Sub acima.

Quando as etapas forem concluídas, o projeto poderá acessar as APIs. Agora é hora de atualizar o aplicativo para usar essas APIs.

4. Crie recursos do Pub/Sub

Recapitulando a ordem de sequência do fluxo de trabalho da Fila de tarefas do Módulo 18:

  1. O módulo 18 usou o arquivo queue.yaml para criar uma fila pull chamada pullq.
  2. O aplicativo adiciona tarefas à fila pull para rastrear os visitantes.
  3. Em algum momento, as tarefas são processadas por um worker e alocadas por um período finito de tempo (uma hora).
  4. As tarefas são executadas para calcular as contagens de visitantes recentes.
  5. As tarefas são excluídas da fila após a conclusão.

Você vai replicar um fluxo de trabalho semelhante com o Pub/Sub. A próxima seção apresenta a terminologia básica do Pub/Sub, com três maneiras diferentes de criar os recursos necessários do Pub/Sub.

Terminologia de fila de tarefas (pull) do App Engine x Cloud Pub/Sub

Mudar para o Pub/Sub requer um pequeno ajuste no seu vocabulário. Abaixo estão as categorias principais e os termos relevantes de ambos os produtos. Consulte também o guia de migração, que apresenta comparações semelhantes.

  • Estrutura de dados em fila: com a fila de tarefas, os dados são colocados em filas pull. no Pub/Sub, os dados vão para tópicos.
  • Unidades de dados em fila: tarefas pull com fila de tarefas são chamadas de mensagens com o Pub/Sub.
  • Processadores de dados: com a fila de tarefas, os workers acessam as tarefas pull. com o Pub/Sub, é preciso ter assinaturas/assinantes para receber mensagens.
  • Extração de dados: alocar de uma tarefa pull é o mesmo que extrair uma mensagem de um tópico (por meio de uma assinatura).
  • Limpeza/conclusão: excluir uma tarefa da fila de tarefas de uma fila pull após o término é análogo a reconhecer uma mensagem do Pub/Sub

O produto na fila muda, mas o fluxo de trabalho permanece relativamente semelhante:

  1. Em vez de uma fila pull, o app usa um tópico chamado pullq.
  2. Em vez de adicionar tarefas a uma fila pull, o app envia mensagens para um tópico (pullq).
  3. Em vez de um worker alocar tarefas da fila pull, um assinante chamado worker recebe mensagens do tópico pullq.
  4. O aplicativo processa payloads de mensagens, incrementando as contagens de visitantes no Datastore.
  5. Em vez de excluir tarefas da fila pull, o app confirma as mensagens processadas.

Com a fila de tarefas, a configuração envolve a criação da fila pull. Com o Pub/Sub, a configuração requer a criação de um tópico e uma assinatura. No Módulo 18, processamos queue.yaml fora da execução do app. o mesmo precisa ser feito com o Pub/Sub.

Há três opções para criar tópicos e assinaturas:

  1. No console do Cloud
  2. Na linha de comando, ou
  3. Do código (script Python curto)

Escolha uma das opções abaixo e siga as instruções correspondentes para criar seus recursos do Pub/Sub.

No console do Cloud

Para criar um tópico no Console do Cloud, siga estas etapas:

  1. Acesse a página de tópicos do Pub/Sub do console do Cloud.
  2. Clique em Criar tópico na parte de cima. uma nova caixa de diálogo é aberta (veja a imagem abaixo).
  3. No campo ID do tópico, insira pullq.
  4. Desmarque todas as opções e selecione Chave de criptografia gerenciada pelo Google.
  5. Clique no botão Criar tópico.

A caixa de diálogo de criação de tópicos é assim:

a05cfdbf64571ceb.png

Agora que você tem um tópico, crie uma assinatura para ele:

  1. Acesse a página Assinaturas do Pub/Sub no console do Cloud.
  2. Clique em Criar assinatura na parte de cima da tela. Veja a imagem abaixo.
  3. Digite worker no campo ID da assinatura.
  4. Escolha pullq no menu suspenso Selecionar um tópico do Cloud Pub/Sub, anotando o "nome do caminho totalmente qualificado". por exemplo, projects/PROJECT_ID/topics/pullq
  5. Em Tipo de entrega, selecione Pull.
  6. Deixe todas as outras opções como estão e clique no botão Criar.

A tela de criação de assinatura vai ficar assim:

c5444375c20b0618.jpeg

Você também pode criar uma inscrição na página Tópicos, com este "atalho" podem ser úteis para associar temas a assinaturas. Para saber mais sobre como criar assinaturas, consulte a documentação.

Na linha de comando,

Os usuários do Pub/Sub podem criar tópicos e assinaturas com os comandos gcloud pubsub topics create TOPIC_ID e gcloud pubsub subscriptions create SUBSCRIPTION_ID --topic=TOPIC_ID, respectivamente. A execução deles com um TOPIC_ID de pullq e um SUBSCRIPTION_ID de worker resulta na seguinte saída para o projeto 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].

Consulte também esta página na documentação do guia de início rápido. Usar a linha de comando pode simplificar fluxos de trabalho em que tópicos e assinaturas são criados regularmente, e esses comandos podem ser usados em scripts de shell para essa finalidade.

Do código (script Python curto)

Outra maneira de automatizar a criação de tópicos e assinaturas é usar a API Pub/Sub no código-fonte. Confira abaixo o código do script maker.py na pasta de repositórios do 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()

A execução desse script gera o resultado esperado (desde que não haja erros):

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

Chamar a API para criar recursos já existentes resulta em uma exceção google.api_core.exceptions.AlreadyExists gerada pela biblioteca de cliente, processada normalmente pelo script:

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

Se você ainda não conhece o Pub/Sub, consulte o artigo sobre a arquitetura do Pub/Sub para mais informações.

5. Atualizar a configuração

As atualizações na configuração incluem a alteração de vários arquivos de configuração, bem como a criação do equivalente de filas pull do App Engine, mas dentro do ecossistema Cloud Pub/Sub.

Excluir o arquivo row.yaml

Estamos deixando de usar a fila de tarefas por completo. Portanto, exclua queue.yaml porque o Pub/Sub não usa esse arquivo. Em vez de criar uma fila pull, crie um tópico (e uma assinatura) do Pub/Sub.

requirements.txt

Anexe google-cloud-ndb e google-cloud-pubsub a requirements.txt para mesclar flask do Módulo 18. O requirements.txt do Módulo 19 atualizado ficará assim:

flask
google-cloud-ndb
google-cloud-pubsub

Este arquivo requirements.txt não tem números de versão, o que significa que as versões mais recentes foram selecionadas. Se surgir alguma incompatibilidade, siga a prática padrão de usar números de versão para fixar versões de trabalho de um app.

app.yaml

As mudanças no app.yaml serão diferentes se você estiver usando o Python 2 ou atualizando para o Python 3.

Python 2

A atualização acima para requirements.txt adiciona o uso de bibliotecas de cliente do Google Cloud. Eles exigem suporte adicional do App Engine, como algumas bibliotecas integradas, setuptools e grpcio. O uso de bibliotecas integradas requer uma seção libraries no app.yaml e números de versão da biblioteca ou "latest" para acessar o mais recente disponível nos servidores do App Engine. O app.yaml do módulo 18 ainda não tem uma destas seções:

ANTES:

runtime: python27
threadsafe: yes
api_version: 1

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

Adicione uma seção libraries a app.yaml com entradas para setuptools e grpcio, selecionando as versões mais recentes. Adicione também uma entrada runtime de marcador de posição para Python 3, comentada junto com uma versão 3.x atual, por exemplo, 3.10, no momento da criação deste codelab. Com essas mudanças, o app.yaml agora vai ficar assim:

DEPOIS:

#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 usuários do Python 3 e do app.yaml, o que importa é a remoção. Nesta seção, você vai excluir a seção handlers e as diretivas threadsafe e api_version, sem criar uma seção libraries.

Os ambientes de execução de segunda geração não oferecem bibliotecas integradas de terceiros. Portanto, uma seção libraries não é necessária no app.yaml. Além disso, a cópia (também conhecida como disponibilização de fornecedores ou autoagrupamento) de pacotes de terceiros não integrados não é mais necessária. Você só precisa listar as bibliotecas de terceiros que o app usa no requirements.txt.

A seção handlers em app.yaml é para especificar o aplicativo (script) e os gerenciadores de arquivos estáticos. Como o ambiente de execução do Python 3 exige que frameworks da Web executem o próprio roteamento, todos os gerenciadores de script precisam ser alterados para auto. Se o app (como o do Módulo 18) não veicular arquivos estáticos, todas as rotas vão ser auto, tornando-as irrelevantes. Como resultado, a seção handlers também não é necessária. Portanto, a exclua.

Por fim, as diretivas threadsafe e api_version não são usadas no Python 3. Portanto, elas também podem ser excluídas. Como resultado, você precisa excluir todas as seções de app.yaml para que apenas a diretiva runtime permaneça, especificando uma versão moderna do Python 3, por exemplo, 3.10. Veja como o app.yaml vai ficar antes e depois dessas atualizações:

ANTES:

runtime: python27
threadsafe: yes
api_version: 1

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

DEPOIS:

runtime: python310

Para quem ainda não está pronto para excluir tudo do app.yaml para Python 3, fornecemos um arquivo alternativo app3.yaml na pasta de repositórios do Módulo 19. Se você quiser usá-lo em implantações, anexe esse nome de arquivo ao final do seu comando: gcloud app deploy app3.yaml. Caso contrário, o app será implantado por padrão com o arquivo app.yaml do Python 2 que você não alterou.

appengine_config.py

Se você está fazendo upgrade para o Python 3, não é necessário usar a appengine_config.py, então exclua-a. Isso não é necessário porque o suporte a bibliotecas de terceiros exige apenas especificá-las em requirements.txt. Usuários do Python 2, continuem lendo.

O appengine_config.py do Módulo 18 tem o código adequado para oferecer suporte a bibliotecas de terceiros, por exemplo, Flask e as bibliotecas de cliente do Cloud que foram adicionadas ao 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)

No entanto, esse código sozinho não é suficiente para oferecer suporte às bibliotecas integradas recém-adicionadas (setuptools, grpcio). Como algumas linhas extras são necessárias, atualize appengine_config.py para que ele fique assim:

DEPOIS:

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)

Mais detalhes sobre as mudanças necessárias para oferecer suporte às bibliotecas de cliente do Cloud podem ser encontrados na documentação sobre a migração de serviços em pacote.

Outras atualizações de configuração

Se você tiver uma pasta lib, exclua-a. Se você for usuário do Python 2, reabasteça a pasta lib executando o seguinte comando:

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

Se você tiver o Python 2 e o 3 instalados no seu sistema de desenvolvimento, talvez seja necessário usar pip2 em vez de pip.

6. Modificar o código do aplicativo

Esta seção apresenta atualizações para o arquivo principal do aplicativo, main.py, que substitui o uso de filas pull da fila de tarefas do App Engine pelo Cloud Pub/Sub. Não há mudanças no modelo da Web, templates/index.html. Os dois apps precisam funcionar de forma idêntica, mostrando os mesmos dados.

Atualizar importações e inicialização

Há várias atualizações para importações e inicialização:

  1. Para as importações, substitua o ambiente do App Engine e a fila de tarefas pelo BGP e o Pub/Sub.
  2. Renomeie pullq de um nome QUEUE para um nome de TOPIC.
  3. Com as tarefas de pull, o worker as alugou por uma hora. No entanto, com o Pub/Sub, os tempos limite são medidos por mensagem. Portanto, exclua a constante HOUR.
  4. As APIs do Cloud exigem o uso de um cliente de API. Portanto, inicie-as para o Cloud NBS e o Cloud Pub/Sub, sendo que este último fornece clientes para tópicos e assinaturas.
  5. O Pub/Sub exige o ID do projeto do Cloud. Portanto, importe-o e receba-o de google.auth.default().
  6. O Pub/Sub exige "nomes de caminho totalmente qualificados" para tópicos e assinaturas, então crie esses usando as funções de conveniência *_path().

Confira abaixo as importações e a inicialização do módulo 18, seguidas de como as seções vão ficar após a implementação das mudanças acima, com a maior parte do novo código sendo vários recursos do 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__)

DEPOIS:

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)

Atualizações do modelo de dados da visita

O modelo de dados Visit não muda. O acesso ao Datastore requer o uso explícito do gerenciador de contexto do cliente da API Cloud NBS, ds_client.context(). No código, isso significa que você envolve as chamadas do Datastore em store_visit() e fetch_visits() dentro de blocos with do Python. Essa atualização é idêntica à abordada no Módulo 2.

A mudança mais relevante para o Pub/Sub é substituir o enfileiramento de uma tarefa de pull na fila de tarefas pela publicação de uma mensagem do Pub/Sub no tópico pullq. Confira abaixo o código antes e depois dessas atualizações:

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)

DEPOIS:

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)

Atualizações do modelo de dados de VisitorCount

O modelo de dados VisitorCount não muda e realiza fetch_counts(), exceto para envolver sua consulta do Datastore dentro de um bloco with, conforme ilustrado abaixo:

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)

DEPOIS:

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)

Atualizar código do worker

O código do worker é atualizado a partir do momento em que é substituído pelo Cloud NFS e o Task Queue pelo Pub/Sub. No entanto, o fluxo de trabalho continua o mesmo.

  1. Una as chamadas do Datastore no bloco with do gerenciador de contexto do Cloud NBS.
  2. A limpeza da fila de tarefas envolve a exclusão de todas as tarefas da fila pull. Com o Pub/Sub, "IDs de confirmação" são coletados em acks e excluídos/confirmados no final.
  3. As tarefas pull da fila de tarefas são concedidas de maneira semelhante às mensagens do Pub/Sub. Enquanto a exclusão de tarefas de pull é feita com os próprios objetos de tarefa, as mensagens do Pub/Sub são excluídas por meio dos IDs de confirmação.
  4. Os payloads de mensagens do Pub/Sub exigem bytes (não strings do Python). Portanto, há alguma codificação e decodificação UTF-8 ao publicar e extrair mensagens de um tópico, respectivamente.

Substitua log_visitors() pelo código atualizado abaixo, implementando as mudanças descritas:

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

DEPOIS:

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

Não há mudanças no gerenciador principal do aplicativo root(). Nenhuma mudança é necessária no arquivo de modelo HTML templates/index.html. Portanto, isso une todas as atualizações necessárias. Parabéns por chegar ao novo aplicativo do Módulo 19 usando o Cloud Pub/Sub.

7. Resumo/limpeza

Implante seu app para verificar se ele funciona conforme o esperado e em qualquer saída refletida. Além disso, execute o worker para processar as contagens de visitantes. Após a validação do app, execute as etapas de limpeza e considere as próximas etapas.

Implante e verifique o aplicativo

Verifique se você já criou o tópico pullq e a assinatura worker. Se isso tiver sido concluído e o app de exemplo estiver pronto, implante o app com gcloud app deploy. A saída será idêntica à do app Module 18, mas você terá substituído todo o mecanismo de enfileiramento:

b667551dcbab1a09.png

O front-end da Web do app agora verifica se essa parte do aplicativo funciona. Embora essa parte do app consulte e mostre os principais visitantes e as visitas mais recentes, ele registra essa visita e cria uma tarefa pull para adicionar esse visitante à contagem geral. Essa tarefa agora está na fila aguardando para ser processada.

Você pode executar esse comando com um serviço de back-end do App Engine, um job cron, navegar para /log ou emitir uma solicitação HTTP de linha de comando. Este é um exemplo de execução de como chamar o código do worker por curl (substitua por PROJECT_ID):

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

A contagem atualizada será refletida na próxima visita ao site. Pronto!

Limpar

Geral

Se você já tiver terminado por enquanto, recomendamos que desative seu aplicativo do App Engine para evitar cobranças. No entanto, se você quiser fazer mais testes, saiba que a plataforma do App Engine tem uma cota sem custo financeiro e, desde que você não exceda esse nível de uso, não haverá cobranças. Isso é para computação, mas também pode haver cobranças por serviços relevantes do App Engine. Portanto, consulte a página de preços para mais informações. Se essa migração envolver outros serviços do Cloud, eles serão faturados separadamente. Em ambos os casos, se aplicável, consulte a seção "Específico para este codelab". seção abaixo.

Para divulgação completa, a implantação em uma plataforma de computação sem servidor do Google Cloud, como o App Engine, incorre em menores custos de criação e armazenamento. O Cloud Build tem a própria cota sem custo financeiro, assim como o Cloud Storage. O armazenamento da imagem consome parte da cota. No entanto, talvez você more em uma região que não tenha esse nível sem custo financeiro, portanto, esteja ciente do uso do armazenamento para minimizar os possíveis custos. "Pastas" específicas do Cloud Storage que você deve analisar incluem:

  • 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
  • Os links de armazenamento acima dependem do PROJECT_ID e da *LOC*ação, por exemplo, "us" caso seu app esteja hospedado nos EUA.

Por outro lado, se você não for continuar com este aplicativo ou outros codelabs de migração relacionados e quiser excluir tudo completamente, encerre seu projeto.

Específicos deste codelab

Os serviços listados abaixo são exclusivos deste codelab. Consulte a documentação de cada produto para mais informações:

  • Diferentes componentes do Cloud Pub/Sub têm um nível sem custo financeiro. Determine seu uso geral para ter uma ideia melhor das implicações de custo e consulte a página de preços para mais detalhes.
  • o serviço App Engine Datastore é fornecido pelo Cloud Datastore (Cloud Firestore no modo Datastore), que também tem um nível sem custo financeiro; consulte a página de preços para mais informações.

Próximas etapas

Além deste tutorial, outros módulos de migração que se concentram em abandonar os serviços em pacote legados a considerar incluem:

  • Módulo 2: migrar do App Engine ndb para o Cloud NBS
  • Módulos 7 a 9: migrar da fila de tarefas do App Engine (tarefas push) para o Cloud Tasks
  • Módulos 12 a 13: migrar do Memcache do App Engine para o Cloud Memorystore
  • Módulos 15 a 16: migrar do App Engine Blobstore para o Cloud Storage

O App Engine não é mais a única plataforma sem servidor do Google Cloud. Se você tem um aplicativo pequeno do App Engine ou com funcionalidade limitada e quer transformá-lo em um microsserviço independente ou quer dividir um aplicativo monolítico em vários componentes reutilizáveis, estes são bons motivos para migrar para o Cloud Functions. Se a conteinerização se tornou parte do fluxo de trabalho de desenvolvimento de aplicativos, principalmente se consistir em um pipeline de CI/CD (integração/entrega contínua ou implantação), considere migrar para o Cloud Run. Esses cenários são abordados nos seguintes módulos:

  • Migrar do App Engine para o Cloud Functions: consulte o Módulo 11
  • Migrar do App Engine para o Cloud Run: consulte o Módulo 4 para conteinerizar seu app com o Docker ou o Módulo 5 para fazer isso sem contêineres, conhecimento do Docker ou Dockerfiles

A mudança para outra plataforma sem servidor é opcional. Recomendamos considerar as melhores opções para seus apps e casos de uso antes de fazer qualquer mudança.

Seja qual for o módulo de migração que você considerar a seguir, todo o conteúdo da estação de migração sem servidor (codelabs, vídeos, código-fonte [quando disponível]) pode ser acessado no repositório de código aberto. O README do repositório também fornece orientações sobre quais migrações considerar e qualquer "ordem" relevante. de módulos de migração.

8. Outros recursos

Abaixo estão listados recursos adicionais para desenvolvedores que querem explorar melhor esse Módulo de migração ou o relacionado, assim como produtos relacionados. Isso inclui locais para fornecer feedback sobre esse conteúdo, links para o código e várias documentações que podem ser úteis.

Problemas/feedback sobre codelabs

Se você encontrar problemas com este codelab, pesquise seu problema antes de preenchê-lo. Links para pesquisar e criar novos problemas:

Recursos de migração

Os links para as pastas do repositório do Módulo 18 (INÍCIO) e do Módulo 19 (FINISH) podem ser encontrados na tabela abaixo.

Codelab

Python 2

Python 3

Módulo 18

código

(n/a)

Módulo 19 (este codelab)

código

(igual ao Python 2, mas use o app3.yaml, a menos que você tenha atualizado o app.yaml conforme abordado acima)

Referências on-line

Confira abaixo os recursos relevantes para este tutorial:

Filas de tarefas do App Engine

Cloud Pub/Sub

App Engine MapReduce e Cloud NBS (Datastore)

Plataforma do App Engine

Outras informações sobre a nuvem

Vídeos

Licença

Este conteúdo está sob a licença Atribuição 2.0 Genérica da Creative Commons.