Módulo 8: migrar do App Engine ndb e taskqueue para o Cloud NDB e o Cloud Tasks

Esta série de codelabs (tutoriais práticos e autoguiados) destina-se a ajudar os desenvolvedores do Google App Engine (Padrão) a modernizar os aplicativos por meio de uma série de migrações. A etapa mais significativa é deixar de usar os serviços originais de pacote em tempo de execução porque os ambientes de execução de última geração são mais flexíveis, oferecendo aos usuários uma variedade maior de opções de serviços. A migração para o ambiente de execução de geração mais recente permite uma integração com os produtos do Google Cloud mais facilmente, usa uma variedade maior de serviços compatíveis e oferece suporte às versões de linguagem atuais.

Esse codelab ajuda os usuários a migrar de tarefas push do App Engine e de sua API/biblioteca taskqueue para o Cloud Tasks. Se seu aplicativo não usa filas de tarefas, você pode usar este codelab como um exercício para saber como migrar tarefas push do App Engine para o Cloud Tasks.

Você aprenderá como realizar as seguintes tarefas:

  • Migrar do App Engine taskqueue para o Cloud Tasks
  • Criar tarefas push com o Cloud Tasks
  • Migrar do App Engine ndb para o Cloud NDB (mesmo que o Módulo 2)

Pré-requisitos

Pesquisa

Como você usará este codelab?

Apenas leitura Leitura e exercícios

Como adicionamos tarefas push do App Engine ao aplicativo de amostra no codelab anterior (Módulo 7), agora podemos migrá-lo para o Cloud Tasks. Estes são os principais passos para a migração deste tutorial:

  1. Configuração/Pré-trabalho
  2. Atualizar os arquivos de configuração
  3. Atualizar aplicativo principal

Antes de prosseguirmos com a parte principal do tutorial, vamos configurar o nosso projeto, obter o código e, em seguida, implantar o aplicativo de referência para saber que começamos a trabalhar com o código em funcionamento.

1. Configurar projeto

Recomendamos que você reutilize o mesmo projeto usado para concluir o Módulo 7. Se preferir, crie um novo projeto ou reutilize outro. Verifique se o projeto tem uma conta de faturamento ativa e o App Engine (aplicativo) está ativado.

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

Um dos pré-requisitos para este codelab é ter um aplicativo de amostra do Módulo 7. Se você não tiver um, recomendamos concluir o tutorial do Módulo 7 (link acima) antes de prosseguir. Caso contrário, se você já estiver familiarizado com o conteúdo, comece pegando o código do Módulo 7 abaixo.

Não importa se você usa o seu ou o nosso, o código do Módulo 7 será onde vamos. Este codelab do Módulo 2 orienta você em cada etapa e, quando concluído, deve ser semelhante ao código no ponto FINISH (incluindo uma porta opcional do Python 2 para 3).

O diretório dos arquivos do Módulo 7 (seu ou de nosso) deve ter a seguinte aparência:

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

Se você concluiu o tutorial do Módulo 7, você também terá uma pasta lib com o Flask e as dependências correspondentes.

3. (Re)Implantar aplicativo do módulo 7

As etapas de pré-trabalho restantes para serem executadas agora:

  1. Familiarize-se com a ferramenta de linha de comando do gcloud (se necessário).
  2. (Re)implantar o código do Módulo 7 no App Engine (se necessário)

Depois que você concluir essas etapas e confirmar que está em operação, seguiremos neste tutorial, começando com os arquivos de configuração.

requirements.txt

O requirements.txt do Módulo 7 lista somente o Flask como um pacote obrigatório. O Cloud NDB e o Cloud Tasks têm as próprias bibliotecas de cliente. Portanto, nesta etapa, adicione esses pacotes a requirements.txt para que ele fique assim:

Flask==1.1.2
google-cloud-ndb==1.7.1
google-cloud-tasks==1.5.0

Recomendamos o uso das versões mais recentes de cada biblioteca. Os números de versão acima são as mais recentes para o Python 2 no momento da redação deste tutorial. Os pacotes equivalentes do Python 3 estarão em versões mais recentes. O código na pasta do repositório FINISH é atualizado com mais frequência e pode ter versões mais recentes, embora isso seja menos provável para as bibliotecas do Python 2 que geralmente são congeladas.

app.yaml

Consulte as bibliotecas integradas grpcio e setuptools em app.yaml em uma seção libraries:

libraries:
- name: grpcio
  version: 1.0.0
- name: setuptools
  version: 36.6.0

appengine_config.py

Atualize appengine_config.py para usar pkg_resources para vincular essas bibliotecas integradas às bibliotecas de terceiros copiadas, como Flask e bibliotecas de cliente do Google Cloud:

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)

Há apenas um arquivo de aplicativo, main.py, então todas as alterações nesta seção afetam apenas esse arquivo.

Atualizar importações e inicialização

No momento, nosso app usa as bibliotecas google.appengine.api.taskqueue e google.appengine.ext.ndb integradas:

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

Substitua ambos por google.cloud.ndb e google.cloud.tasks. Além disso, o Cloud Tasks requer a codificação JSON do payload da tarefa, portanto, também importe json. Quando terminar, veja a aparência da seção import de main.py:

  • Depois:
from datetime import datetime
import json
import logging
import time
from flask import Flask, render_template, request
from google.cloud import ndb, tasks

Migrar para o Cloud Tasks (e o Cloud NDB)

  • Antes:
def store_visit(remote_addr, user_agent):
    'create new Visit entity in Datastore'
    Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

Não há alterações no store_visit() além do que você fez no Módulo 2: adicione um gerenciador de contexto a todo o acesso ao Datastore. Isso ocorre na forma de criar uma nova entidade Visit encapsulada em uma instrução with.

  • Depois:
def store_visit(remote_addr, user_agent):
    'create new Visit entity in Datastore'
    with ds_client.context():
        Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

No momento, o Cloud Tasks exige a ativação do App Engine no projeto do Google Cloud para que você possa usá-lo (mesmo se não tiver o código do App Engine). Caso contrário, as filas de tarefas não funcionarão. Consulte esta seção nos documentos para mais informações. O Cloud Tasks é compatível com tarefas em execução no App Engine (destinos do App Engine), mas também pode ser executado em qualquer endpoint HTTP (destinos HTTP) com um endereço IP público, como Cloud Functions, Cloud Run, GKE, Compute Engine ou até mesmo um servidor da Web local. Nosso aplicativo simples usa um alvo do App Engine para tarefas.

Algumas configurações são necessárias para usar o Cloud NDB e o Cloud Tasks. Na parte superior de main.py, na inicialização do Flask, inicialize o Cloud NDB e o Cloud Tasks. Defina também algumas constantes que indicam onde suas tarefas push serão executadas.

app = Flask(__name__)
ds_client = ndb.Client()
ts_client = tasks.CloudTasksClient()

PROJECT_ID = 'PROJECT_ID'  # replace w/your own
REGION = 'REGION'    # replace w/your own
QUEUE_NAME = 'default'     # replace w/your own if desired
QUEUE_PATH = ts_client.queue_path(PROJECT_ID, REGION, QUEUE_NAME)

Depois de criar a fila de tarefas, preencha o PROJECT_ID do projeto, o REGION em que as tarefas serão executadas (precisam ser as mesmas da região do App Engine) e nome da sua fila push. O App Engine apresenta uma fila "default", então usaremos esse nome (mas não é obrigatório).

A fila default é especial e criada automaticamente em determinadas circunstâncias. Uma delas é ao usar APIs do App Engine, portanto, se você (re)usar o mesmo projeto que o Módulo 7, default já existe. No entanto, se você criou um novo projeto especificamente para o Módulo 8, será necessário criar default manualmente. Mais informações sobre a fila do default podem ser encontradas na documentação do queue.yaml.

A finalidade do ts_client.queue_path() é criar o "nome de caminho totalmente qualificado" de uma fila de tarefas (QUEUE_PATH) necessário para criar uma tarefa. Também é necessária uma estrutura JSON especificando os parâmetros da tarefa:

task = {
    'app_engine_http_request': {
        'relative_uri': '/trim',
        'body': json.dumps({'oldest': oldest}).encode(),
        'headers': {
            'Content-Type': 'application/json',
        },
    }
}

O que você está observando acima?

  1. Forneça as informações do destino da tarefa:
    • Para destinos do App Engine, especifique app_engine_http_request como o tipo de solicitação e relative_uri é o gerenciador de tarefas do App Engine.
    • Para destinos HTTP, use http_request e url.
  2. body: os parâmetros de codificação JSON e Unicode codificados em string para enviar à tarefa (push)
  3. Especificar explicitamente o cabeçalho Content-Type codificado em JSON

Consulte a documentação para ver mais informações sobre suas opções aqui.

Com a configuração, vamos atualizar o app fetch_visits(). Esta é a aparência do tutorial anterior:

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

As atualizações necessárias:

  1. Mudar do App Engine ndb para o Cloud NDB
  2. Novo código para extrair o carimbo de data/hora da visita mais antiga exibida
  3. Use o Cloud Tasks para criar uma nova tarefa em vez do taskqueue do App Engine

Esta é a nova aparência do seu fetch_visits():

  • Depois:
def fetch_visits(limit):
    'get most recent visits and add task to delete older visits'
    with ds_client.context():
        data = Visit.query().order(-Visit.timestamp).fetch(limit)
    oldest = time.mktime(data[-1].timestamp.timetuple())
    oldest_str = time.ctime(oldest)
    logging.info('Delete entities older than %s' % oldest_str)
    task = {
        'app_engine_http_request': {
            'relative_uri': '/trim',
            'body': json.dumps({'oldest': oldest}).encode(),
            'headers': {
                'Content-Type': 'application/json',
            },
        }
    }
    ts_client.create_task(parent=QUEUE_PATH, task=task)
    return (v.to_dict() for v in data), oldest_str

Resumindo a atualização do código:

  • Mudar para o Cloud NDB significa mover o código do Datastore em uma instrução with
  • Alternar para o Cloud Tasks significa usar ts_client.create_task() em vez de taskqueue.add()
  • Transmita o caminho completo da fila e o payload task (descrito anteriormente)

Atualizar o gerenciador de tarefas (push)

Há poucas alterações que precisam ser feitas na função de gerenciador de tarefas (push).

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

A única coisa que precisa ser feita é colocar todo o acesso ao Datastore na instrução with do gerenciador de contexto, tanto a consulta quanto pela solicitação de exclusão. Com isso em mente, atualize seu gerenciador trim() desta maneira:

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

Não há mudanças em templates/index.html neste ou no próximo codelab.

Implante o aplicativo

Verifique todas as alterações que o código compila e implanta novamente. Confirme se o aplicativo (ainda) funciona. Você deve esperar uma saída idêntica ao do Módulo 7. Como você acabou de reconectar as configurações, o processo continua funcionando como esperado.

Se você passou para este tutorial sem fazer o codelab do Módulo 7, o aplicativo em si não muda. Ele registra todas as visitas à página principal da web (/) e tem esta aparência quando você visita o site vezes, e informa que excluiu todas as visitas anteriores ao décimo:

App visitme do módulo 7

Isso conclui este codelab. Agora, seu código deve corresponder ao conteúdo no Repositório do módulo 8. Parabéns pela conclusão das migrações mais importantes das tarefas push! O módulo 9 (link do codelab abaixo) é opcional, ajudando os usuários a migrar para o Python 3 e o Cloud Datastore.

Opcional: limpar

E a limpeza para evitar cobrança até que você esteja pronto para passar para o próximo codelab de migração? Como você, os desenvolvedores já estão atualizados nas informações de preços do App Engine.

Opcional: desativar app

Se você ainda não estiver pronto para avançar para o próximo tutorial, desative seu app para evitar cobranças. Quando estiver pronto para passar para o próximo codelab, você poderá reativá-lo. Enquanto seu aplicativo estiver desativado, ele não receberá tráfego para custos. No entanto, o uso do Datastore pode ser cobrado se exceder a cota gratuita, Exclua o suficiente para ficar abaixo desse limite.

Por outro lado, se você não quiser continuar com as migrações e quiser excluir tudo completamente, poderá encerrar seu projeto.

Próximas etapas

Além deste tutorial, a próxima etapa é o Módulo 9 e seu codelab, além da portabilidade para o Python 3. É um pouco opcional porque nem todos estão prontos para a etapa. Há também uma porta opcional do Cloud NDB para o Cloud Datastore. Ela é definitivamente e somente para quem quer sair do NDB e consolidar o código que usa o Cloud Datastore. Essa migração é idêntica ao Codelab da migração do Módulo 3.

  • Módulo 9 Migre do Python 2 para 3 e do Cloud NDB para o Cloud Datastore
    • Portabilidade de módulo de migração opcional para o Python 3
    • Também inclui migração opcional do Cloud NDB para o Cloud Datastore (igual ao Módulo 3) e
    • uma pequena migração do Cloud Tasks v1 para a v2, uma vez que a biblioteca de cliente está congelada para o Python 2.
  • Módulo 4: migre para o Cloud Run com o Docker
    • Contentorize seu app para ser executado no Cloud Run com o Docker
    • Essa migração permite que você permaneça no Python 2.
  • Módulo 5: Migrar para o Cloud Run com o Cloud Buildpacks
    • Contentorize seu app para ser executado no Cloud Run com o Cloud Buildpacks
    • Não é preciso saber nada sobre o Docker, os contêineres ou as Dockerfiles.
    • Requer que seu app já tenha sido migrado para o Python 3 (o Buildpack não é compatível com o Python 2)
  • Módulo 6: Migrar para o Cloud Firestore
    • Migrar para o Cloud Firestore para acessar recursos do Firebase
    • O Cloud Firestore é compatível com o Python 2, mas este codelab está disponível apenas no Python 3.

Problemas/comentários do módulo de migração do App Engine

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 7 (START) e Módulo 8 (FINISH) podem ser encontrados na tabela abaixo. Elas também podem ser acessadas no repositório de todas as migrações de codelab do App Engine, que você pode clonar ou fazer o download de um arquivo ZIP.

Codelab

Python 2

Python 3

Módulo 7

código

(n/a)

Módulo 8

código

(n/a)

Recursos do App Engine

Veja abaixo mais recursos relacionados a essa migração específica: