Como usar a fila de tarefas do App Engine (tarefas pull) em apps Flask (módulo 18)

1. Visão geral

A série de codelabs da Serverless Migration Station (tutoriais práticos e autoguiados) e os vídeos relacionados têm como objetivo ajudar os desenvolvedores sem servidor do Google Cloud a modernizar os aplicativos orientando-os em uma ou mais migrações, principalmente a migração de serviços legados. Isso torna seus apps mais portáteis e oferece mais opções e flexibilidade, permitindo que você se integre e acesse uma variedade maior de produtos do Cloud e faça upgrade mais fácil para versões de linguagem mais recentes. Embora o foco inicial seja nos 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 em outro lugar, se aplicável.

Este codelab ensina como incluir e usar tarefas pull da fila de tarefas do App Engine no app de exemplo do codelab do módulo 1. Vamos adicionar o uso de tarefas de pull neste tutorial do módulo 18 e migrar esse uso para o Cloud Pub/Sub no módulo 19. Quem usa filas de tarefas para tarefas push vai migrar para o Cloud Tasks e precisa consultar os módulos de 7 a 9.

Você vai aprender a

  • Usar a API/serviço agrupado da fila de tarefas do App Engine
  • Adicionar o uso da fila pull a um app básico do Python 2 Flask App Engine NDB

O que é necessário

Pesquisa

Como você vai usar este tutorial?

Apenas leitura 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

Para migrar das tarefas pull da fila de tarefas do App Engine, adicione o uso delas ao app Flask e App Engine NDB resultante do codelab do módulo 1. O app de exemplo mostra as visitas mais recentes ao usuário final. Isso é bom, mas é ainda mais interessante acompanhar os visitantes para saber quem acessa mais.

Embora possamos usar tarefas de push para essas contagens de visitantes, queremos dividir a responsabilidade entre o app de exemplo, cuja função é registrar visitas e responder imediatamente aos usuários, e um "trabalhador" designado, cuja função é somar as contagens de visitantes fora do fluxo de trabalho normal de solicitação-resposta.

Para implementar esse design, vamos adicionar o uso de filas de extração ao aplicativo principal e oferecer suporte à funcionalidade do worker. O worker pode ser executado como um processo separado (como uma instância de back-end ou um código em execução em uma VM sempre ativa), um cron job ou uma solicitação HTTP básica de linha de comando usando curl ou wget. Depois dessa integração, você poderá migrar o app para o Cloud Pub/Sub no próximo codelab (Módulo 19).

Este tutorial inclui 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

Essas etapas garantem que você esteja começando com um código funcional.

1. Configurar projeto

Se você concluiu o codelab do Módulo 1, reutilize o mesmo projeto e código. Se preferir, crie um novo projeto ou reutilize outro. Verifique se o projeto tem uma conta de faturamento ativa e um app do App Engine ativado. Encontre o ID do projeto, porque você vai precisar dele várias vezes neste codelab. Use-o sempre que encontrar a variável PROJECT_ID.

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

Um dos pré-requisitos para este codelab é ter um app do App Engine do Módulo 1 funcionando. Conclua o codelab do Módulo 1 (recomendado) ou copie o app do Módulo 1 do repositório. Independentemente de você usar o nosso ou o seu, o código do Módulo 1 é o que vamos "COMEÇAR". Este codelab orienta você em cada etapa, concluindo com um código semelhante ao que está na pasta do repositório do Módulo 18 "FINISH".

Independente do app do Módulo 1 que você usar, a pasta vai ficar parecida com a saída abaixo, possivelmente com uma pasta lib também:

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

3. (Re) Implantar aplicativo de referência

Siga estas etapas para implantar o app do módulo 1:

  1. Exclua a pasta lib se houver uma e execute: pip install -t lib -r requirements.txt para preencher lib novamente. Talvez seja necessário usar o comando pip2 se você tiver o Python 2 e o 3 instalados.
  2. Verifique se você instalou e inicializou a ferramenta de linha de comando gcloud e revisou o uso dela.
  3. Defina seu projeto na nuvem com gcloud config set project PROJECT_ID se não quiser inserir seu PROJECT_ID com cada comando gcloud emitido.
  4. Implante o app de exemplo com gcloud app deploy
  5. Confirme se o app do módulo 1 está funcionando como esperado e mostrando as visitas mais recentes (ilustrado abaixo).

a7a9d2b80d706a2b.png

4. Atualizar a configuração

Não é necessário fazer mudanças nos arquivos de configuração padrão do App Engine (app.yaml, requirements.txt, appengine_config.py). Em vez disso, adicione um novo arquivo de configuração, queue.yaml, com o seguinte conteúdo, colocando-o no mesmo diretório de nível superior:

queue:
- name: pullq
  mode: pull

O arquivo queue.yaml especifica todas as filas de tarefas que existem para seu app, exceto a fila default (push), que é criada automaticamente pelo App Engine. Neste caso, há apenas uma fila pull chamada pullq. O App Engine exige que a diretiva mode seja especificada como pull. Caso contrário, ele cria uma fila push por padrão. Saiba mais sobre como criar filas pull na documentação. Consulte também a página de referência do queue.yaml para outras opções.

Implante esse arquivo separadamente do app. Você ainda vai usar gcloud app deploy, mas também vai fornecer queue.yaml na linha de comando:

$ gcloud app deploy queue.yaml
Configurations to update:

descriptor:      [/tmp/mod18-gaepull/queue.yaml]
type:            [task queues]
target project:  [my-project]

WARNING: Caution: You are updating queue configuration. This will override any changes performed using 'gcloud tasks'. More details at
https://cloud.google.com/tasks/docs/queue-yaml

Do you want to continue (Y/n)?

Updating config [queue]...⠹WARNING: We are using the App Engine app location (us-central1) as the default location. Please use the "--location" flag if you want to use a different location.
Updating config [queue]...done.

Task queues have been updated.

Visit the Cloud Platform Console Task Queues page to view your queues and cron jobs.
$

5. Modificar o código do aplicativo

Esta seção apresenta atualizações nos seguintes arquivos:

  • main.py: adicione o uso de filas pull ao aplicativo principal.
  • templates/index.html: atualize o modelo da Web para mostrar os novos dados.

Importações e constantes

A primeira etapa é adicionar uma nova importação e várias constantes para oferecer suporte a filas de extração:

  • Adicione uma importação da biblioteca da fila de tarefas, google.appengine.api.taskqueue.
  • Adicione três constantes para oferecer suporte à locação do número máximo de tarefas de pull (TASKS) por uma hora (HOUR) da nossa fila pull (QUEUE).
  • Adicione uma constante para mostrar as visitas mais recentes e os principais visitantes (LIMIT).

Confira abaixo o código original e como ele fica após essas atualizações:

ANTES:

from flask import Flask, render_template, request
from google.appengine.ext import ndb

app = Flask(__name__)

AFTER:

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

Adicionar uma tarefa de extração (coletar dados para a tarefa e criar uma tarefa na fila pull)

O modelo de dados Visit permanece o mesmo, assim como a consulta de visitas para exibição em fetch_visits(). A única mudança necessária nessa parte do código é em store_visit(). Além de registrar a visita, adicione uma tarefa à fila pull com o endereço IP do visitante para que o worker possa incrementar o contador de visitantes.

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 entity in Datastore'
    Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

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

AFTER:

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)

Criar um modelo de dados e uma função de consulta para o rastreamento de visitantes

Adicione um modelo de dados VisitorCount para rastrear visitantes. Ele precisa ter campos para o visitor e um número inteiro counter para rastrear o número de visitas. Em seguida, adicione uma nova função (ou um classmethod do Python) chamada fetch_counts() para consultar e retornar os principais visitantes em ordem decrescente. Adicione a classe e a função logo abaixo do corpo de fetch_visits():

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

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

Adicionar código do worker

Adicione uma nova função log_visitors() para registrar os visitantes com uma solicitação GET para /log. Ele usa um dicionário/hash para rastrear as contagens de visitantes mais recentes, alugando o máximo de tarefas possível por uma hora. Para cada tarefa, ele totaliza todas as visitas do mesmo visitante. Com as contagens em mãos, o app atualiza todas as entidades VisitorCount correspondentes já no Datastore ou cria novas, se necessário. A última etapa retorna uma mensagem de texto simples indicando quantos visitantes foram registrados e quantas tarefas foram processadas. Adicione esta função a main.py logo abaixo de fetch_counts():

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

Atualizar o gerenciador principal com novos dados de exibição

Para mostrar os principais visitantes, atualize o manipulador principal root() para invocar fetch_counts(). Além disso, o modelo será atualizado para mostrar o número de principais visitantes e as visitas mais recentes. Empacote as contagens de visitantes com as visitas mais recentes da chamada para fetch_visits() e coloque isso em um único context para transmitir ao modelo da Web. Confira abaixo o código antes e depois dessa mudança:

ANTES:

@app.route('/')
def root():
    'main application (GET) handler'
    store_visit(request.remote_addr, request.user_agent)
    visits = fetch_visits(10)
    return render_template('index.html', visits=visits)

AFTER:

@app.route('/')
def root():
    'main application (GET) handler'
    store_visit(request.remote_addr, request.user_agent)
    context = {
        'limit':  LIMIT,
        'visits': fetch_visits(LIMIT),
        'counts': fetch_counts(LIMIT),
    }
    return render_template('index.html', **context)

Estas são todas as mudanças necessárias em main.py, e aqui está uma representação ilustrativa dessas atualizações para dar uma ideia geral das mudanças que você está fazendo em main.py:

ad5fd3345efc13d0.png

Atualizar o modelo da Web com novos dados de display

O modelo da Web templates/index.html precisa de uma atualização para mostrar os principais visitantes, além da carga normal dos visitantes mais recentes. Solte os principais visitantes e as contagens deles em uma tabela na parte de cima da página e continue renderizando as visitas mais recentes como antes. A única outra mudança é especificar o número mostrado usando a variável limit em vez de codificar o número. Estas são as atualizações que você precisa fazer no modelo da Web:

ANTES:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
<body>

<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

AFTER:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
<body>

<h1>VisitMe example</h1>

<h3>Top {{ limit }} visitors</h3>
<table border=1 cellspacing=0 cellpadding=2>
    <tr><th>Visitor</th><th>Visits</th></tr>
{% for count in counts %}
    <tr><td>{{ count.visitor|e }}</td><td align="center">{{ count.counter }}</td></tr>
{% endfor %}
</table>

<h3>Last {{ limit }} visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

Isso conclui as mudanças necessárias para adicionar o uso de tarefas de extração da fila de tarefas do App Engine ao app de exemplo do módulo 1. Seu diretório agora representa o app de exemplo do módulo 18 e deve conter estes arquivos:

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

6. Resumo/limpeza

Esta seção conclui o codelab implantando o app e verificando se ele funciona conforme o esperado e em qualquer saída refletida. Execute o worker separadamente para processar as contagens de visitantes. Depois da validação do app, faça as etapas de limpeza e considere as próximas etapas.

Implantar e verificar o aplicativo

Verifique se você já configurou sua fila pull, como fizemos na parte de cima deste codelab com gcloud app deploy queue.yaml. Se isso já foi feito e o app de exemplo está pronto, implante o app com gcloud app deploy. A saída deve ser idêntica ao app do módulo 1, exceto que agora ela apresenta uma tabela "principais visitantes" na parte de cima:

b667551dcbab1a09.png

Embora o front-end da Web atualizado mostre os principais visitantes e as visitas mais recentes, as contagens de visitantes não incluem esta visita. O app mostra as contagens de visitantes anteriores enquanto descarta uma nova tarefa que incrementa a contagem desse visitante na fila pull, uma tarefa que está aguardando processamento.

É possível executar a tarefa chamando /log de várias maneiras:

Por exemplo, se você usar curl para enviar uma solicitação GET a /log, a saída será semelhante a esta, considerando que você forneceu seu 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!

Parabéns por concluir este codelab e adicionar o uso do serviço de fila pull da fila de tarefas do App Engine ao app de exemplo. Agora ele está pronto para migrar para o Cloud Pub/Sub, o Cloud NDB e o Python 3 no Módulo 19.

Limpar

Geral

Se você terminou por enquanto, recomendamos que desative o app do App Engine para evitar cobranças. No entanto, se você quiser testar ou experimentar mais, a plataforma do App Engine tem uma cota sem custo financeiro. Portanto, enquanto você não exceder esse nível de uso, não vai receber cobranças. Isso é para computação, mas também pode haver cobranças por serviços relevantes do App Engine. Consulte a página de preços para mais informações. Se essa migração envolver outros serviços do Cloud, eles serão cobrados separadamente. Em qualquer caso, se aplicável, consulte a seção "Específico para este codelab" abaixo.

Para total transparência, a implantação em uma plataforma de computação sem servidor do Google Cloud, como o App Engine, gera custos mínimos de build e armazenamento. O Cloud Build e o Cloud Storage têm cotas sem custo financeiro próprias. O armazenamento dessa imagem usa parte dessa cota. No entanto, talvez você more em uma região que não tem um nível sem custo financeiro. Por isso, fique de olho no uso do armazenamento para minimizar possíveis custos. As "pastas" específicas do Cloud Storage que você precisa 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 da sua PROJECT_ID e *LOC*ação. Por exemplo, "us" se o app estiver hospedado nos EUA.

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

Específico para este codelab

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

Próximas etapas

Nessa "migração", você adicionou o uso da fila push da fila de tarefas ao app de exemplo do Módulo 1, adicionando suporte para rastrear visitantes e implementando o app de exemplo do Módulo 18. Na próxima migração, você vai fazer upgrade das tarefas pull do App Engine para o Cloud Pub/Sub. Desde o final de 2021, os usuários não são mais obrigados a migrar para o Cloud Pub/Sub ao fazer upgrade para o Python 3. Leia mais sobre isso na próxima seção.

Para migrar para o Cloud Pub/Sub, consulte o codelab do módulo 19. Além disso, há outras migrações a serem consideradas, como Cloud Datastore, Cloud Memorystore, Cloud Storage ou Cloud Tasks (filas push). Também há migrações entre produtos para o Cloud Run e o Cloud Functions. Todo o conteúdo da Serverless Migration Station (codelabs, vídeos, código-fonte [quando disponível]) pode ser acessado no repositório de código aberto.

7. Migração para o Python 3

No outono de 2021, a equipe do App Engine estendeu o suporte de muitos dos serviços incluídos para ambientes de execução de segunda geração (que têm um ambiente de execução de primeira geração). Como resultado, não é mais necessário migrar de serviços incluídos, como a fila de tarefas do App Engine, para serviços autônomos do Cloud ou de terceiros, como o Cloud Pub/Sub, ao fazer a portabilidade do app para o Python 3. Em outras palavras, você pode continuar usando a fila de tarefas em apps do App Engine em Python 3, desde que faça o refactoring do código para acessar serviços incluídos de ambientes de execução de próxima geração.

Saiba mais sobre como migrar o uso de serviços em pacote para o Python 3 no codelab do módulo 17 e no vídeo correspondente. Embora esse assunto esteja fora do escopo do módulo 18, abaixo estão as versões em Python 3 do app do módulo 1 portadas para Python 3 e ainda usando o App Engine NDB. Em algum momento, uma versão em Python 3 do app do módulo 18 também será disponibilizada.

8. Outros recursos

Confira abaixo mais recursos para desenvolvedores que querem saber mais sobre este ou outros módulos de migração e produtos relacionados. Isso inclui locais para enviar feedback sobre o conteúdo, links para o código e vários documentos que podem ser úteis.

Problemas/feedback do codelab

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 1 (START) e do Módulo 18 (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. Clone ou faça o download de um arquivo ZIP.

Codelab

Python 2

Python 3

Módulo 1

código

código (não abordado neste tutorial)

Módulo 18 (este codelab)

código

N/A

Referências on-line

Confira abaixo os recursos relevantes para este tutorial:

Filas de tarefas do App Engine

Plataforma do App Engine

Documentação do App Engine

Tempo de execução do Python 2 no App Engine (ambiente padrão)

Tempo de execução do Python 3 no App Engine (ambiente padrão)

Diferenças entre os ambientes de execução do Python 2 e 3 no App Engine (ambiente padrão)

Guia de migração do App Engine (ambiente padrão) do Python 2 para o 3

Informações sobre preços e cotas do App Engine

Lançamento da plataforma App Engine de segunda geração (2018)

Suporte de longo prazo para ambientes de execução legados

Exemplos de migração de documentação

Outras informações da nuvem

Vídeos

Licença

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