Come utilizzare la coda di attività di App Engine (attività di pull) nelle app Flask (modulo 18)

1. Panoramica

La serie di codelab Serverless Migration Station (esercitazioni pratiche e autonome) e i video correlati hanno lo scopo di aiutare gli sviluppatori serverless di Google Cloud a modernizzare le loro applicazioni guidandoli attraverso una o più migrazioni, principalmente abbandonando i servizi legacy. In questo modo, le tue app sono più portatili e hai più opzioni e flessibilità, il che ti consente di integrarti e accedere a una gamma più ampia di prodotti cloud e di eseguire più facilmente l'upgrade alle versioni più recenti del linguaggio. Sebbene inizialmente si concentri sui primi utenti di Cloud, principalmente gli sviluppatori di App Engine (ambiente standard), questa serie è abbastanza ampia da includere altre piattaforme serverless come Cloud Functions e Cloud Run o altrove, se applicabile.

Questo codelab ti insegna a includere e utilizzare le attività pull di App Engine Task Queue nell'app di esempio del codelab del modulo 1. Aggiungiamo il suo utilizzo delle attività di pull in questo tutorial del modulo 18, poi eseguiamo la migrazione di questo utilizzo a Cloud Pub/Sub nel modulo 19. Gli utenti che utilizzano le code di attività per le attività push eseguiranno invece la migrazione a Cloud Tasks e dovranno fare riferimento ai moduli 7-9.

Imparerai a utilizzare

  • Utilizza l'API Task Queue/il servizio integrato di App Engine
  • Aggiungi l'utilizzo della coda pull a un'app Python 2 Flask App Engine NDB di base

Che cosa ti serve

Sondaggio

Come utilizzerai questo tutorial?

Leggilo e basta Leggilo e completa gli esercizi

Come valuteresti la tua esperienza con Python?

Principiante Intermedio Avanzato

Come valuti la tua esperienza di utilizzo dei servizi Google Cloud?

Principiante Intermedio Avanzato

2. Sfondo

Per eseguire la migrazione dalle attività pull di App Engine Task Queue, aggiungi il relativo utilizzo all'app Flask e App Engine NDB esistente risultante dal codelab del modulo 1. L'app di esempio mostra le visite più recenti all'utente finale. Va bene, ma è ancora più interessante monitorare anche i visitatori per vedere chi visita più spesso il tuo canale.

Sebbene potremmo utilizzare push task per questi conteggi dei visitatori, vogliamo dividere la responsabilità tra l'app di esempio, il cui compito è registrare le visite e rispondere immediatamente agli utenti, e un "worker" designato, il cui compito è conteggiare i visitatori al di fuori del normale flusso di lavoro richiesta-risposta.

Per implementare questo design, stiamo aggiungendo l'utilizzo delle code pull all'applicazione principale, oltre a supportare la funzionalità di worker. Il worker può essere eseguito come processo separato (ad esempio un'istanza di backend o un codice in esecuzione su una VM sempre attiva), un job cron o una richiesta HTTP di base da riga di comando utilizzando curl o wget. Dopo questa integrazione, puoi eseguire la migrazione dell'app a Cloud Pub/Sub nel codelab successivo (modulo 19).

Questo tutorial prevede i seguenti passaggi:

  1. Configurazione/preparazione
  2. Aggiorna configurazione
  3. Modificare il codice dell'applicazione

3. Configurazione/preparazione

Questa sezione spiega come:

  1. Configura il progetto cloud
  2. Ottieni l'app di esempio di base
  3. (R)esegui il deployment e convalida l'app di riferimento

Questi passaggi ti assicurano di iniziare con un codice funzionante.

1. Configura il progetto

Se hai completato il codelab del modulo 1, riutilizza lo stesso progetto (e codice). In alternativa, crea un nuovo progetto o riutilizza un altro progetto esistente. Assicurati che il progetto abbia un account di fatturazione attivo e un'app App Engine abilitata. Trova l'ID progetto, in quanto ti servirà più volte in questo codelab e utilizzalo ogni volta che incontri la variabile PROJECT_ID.

2. Ottieni l'app di esempio di base

Uno dei prerequisiti per questo codelab è avere un'app App Engine del Modulo 1 funzionante. Completa il codelab del Modulo 1 (consigliato) o copia l'app del Modulo 1 dal repository. Che tu utilizzi il tuo o il nostro, il codice del Modulo 1 è il punto di "INIZIO". Questo codelab ti guida in ogni passaggio e si conclude con un codice simile a quello presente nella cartella "FINISH" del repository del modulo 18.

Indipendentemente dall'app del Modulo 1 che utilizzi, la cartella dovrebbe essere simile all'output riportato di seguito, possibilmente anche con una cartella lib:

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

3. (Esegui di nuovo il deployment dell'app di base

Per eseguire il deployment dell'app del modulo 1:

  1. Elimina la cartella lib, se presente, ed esegui: pip install -t lib -r requirements.txt per ripopolare lib. Se hai installato sia Python 2 che Python 3, potresti dover utilizzare il comando pip2.
  2. Assicurati di aver installato e inizializzato lo strumento a riga di comando gcloud e di averne esaminato l'utilizzo.
  3. Imposta il tuo progetto cloud con gcloud config set project PROJECT_ID se non vuoi inserire PROJECT_ID con ogni comando gcloud emesso.
  4. Esegui il deployment dell'app di esempio con gcloud app deploy
  5. Verifica che l'app Modulo 1 funzioni come previsto e mostri le visite più recenti (illustrate di seguito).

a7a9d2b80d706a2b.png

4. Aggiorna configurazione

Non sono necessarie modifiche ai file di configurazione standard di App Engine (app.yaml, requirements.txt, appengine_config.py). Aggiungi invece un nuovo file di configurazione, queue.yaml, con i seguenti contenuti, inserendolo nella stessa directory di primo livello:

queue:
- name: pullq
  mode: pull

Il file queue.yaml specifica tutte le code delle attività esistenti per la tua app (ad eccezione della coda default [push] creata automaticamente da App Engine). In questo caso, ce n'è solo una, una coda pull denominata pullq. App Engine richiede che la direttiva mode sia specificata come pull, altrimenti crea una coda push per impostazione predefinita. Scopri di più sulla creazione di code pull nella documentazione. Per altre opzioni, consulta anche la pagina di riferimento di queue.yaml.

Esegui il deployment di questo file separatamente dalla tua app. Continuerai a utilizzare gcloud app deploy, ma dovrai anche fornire queue.yaml sulla riga di 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. Modificare il codice dell'applicazione

Questa sezione include gli aggiornamenti ai seguenti file:

  • main.py: aggiungi l'utilizzo delle code in modalità pull all'applicazione principale
  • templates/index.html: aggiorna il modello web per visualizzare i nuovi dati

Importazioni e costanti

Il primo passaggio consiste nell'aggiungere una nuova importazione e diverse costanti per supportare le code pull:

  • Aggiungi un'importazione della libreria della coda di attività, google.appengine.api.taskqueue.
  • Aggiungi tre costanti per supportare il leasing del numero massimo di attività pull (TASKS) per un'ora (HOUR) dalla nostra coda pull (QUEUE).
  • Aggiungi una costante per visualizzare le visite più recenti e i visitatori principali (LIMIT).

Di seguito è riportato il codice originale e il suo aspetto dopo aver apportato questi aggiornamenti:

PRIMA:

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

app = Flask(__name__)

DOPO:

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

Aggiungi un'attività pull (raccogli i dati per l'attività e crea l'attività nella coda pull)

Il modello di dati Visit rimane invariato, così come la query per le visite da visualizzare in fetch_visits(). L'unica modifica richiesta in questa parte del codice riguarda store_visit(). Oltre a registrare la visita, aggiungi un'attività alla coda pull con l'indirizzo IP del visitatore in modo che il worker possa incrementare il contatore dei visitatori.

PRIMA:

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)

DOPO:

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)

Crea un modello di dati e una funzione di query per il monitoraggio dei visitatori

Aggiungi un modello dati VisitorCount per monitorare i visitatori; deve avere campi per l'visitor stesso e un numero intero counter per monitorare il numero di visite. Poi aggiungi una nuova funzione (in alternativa, può essere un classmethod Python) denominata fetch_counts() per eseguire query e restituire i primi visitatori in ordine decrescente. Aggiungi la classe e la funzione subito sotto il corpo di 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)

Aggiungi codice worker

Aggiungi una nuova funzione log_visitors() per registrare i visitatori tramite una richiesta GET a /log. Utilizza un dizionario/hash per monitorare i conteggi dei visitatori più recenti, assegnando il maggior numero possibile di attività per un'ora. Per ogni attività, conteggia tutte le visite dello stesso visitatore. Con i conteggi a portata di mano, l'app aggiorna tutte le entità VisitorCount corrispondenti già presenti in Datastore o ne crea di nuove, se necessario. L'ultimo passaggio restituisce un messaggio di testo normale che indica il numero di visitatori registrati in base al numero di attività elaborate. Aggiungi questa funzione a main.py subito sotto 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))

Aggiorna l'handler principale con i nuovi dati di visualizzazione

Per visualizzare i principali visitatori, aggiorna l'handler principale root() per richiamare fetch_counts(). Inoltre, il modello verrà aggiornato per mostrare il numero di visitatori principali e le visite più recenti. Raggruppa i conteggi dei visitatori insieme alle visite più recenti dalla chiamata a fetch_visits() e inseriscili in un unico context da passare al modello web. Di seguito è riportato il codice prima e dopo l'applicazione di questa modifica:

PRIMA:

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

DOPO:

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

Queste sono tutte le modifiche richieste da main.py. Di seguito è riportata una rappresentazione illustrativa di questi aggiornamenti per darti un'idea generale delle modifiche che stai apportando a main.py:

ad5fd3345efc13d0.png

Aggiornare il modello web con i nuovi dati display

Il modello web templates/index.html richiede un aggiornamento per visualizzare i visitatori principali oltre al payload normale dei visitatori più recenti. Inserisci i primi visitatori e i relativi conteggi in una tabella nella parte superiore della pagina e continua a visualizzare le visite più recenti come prima. L'unica altra modifica consiste nello specificare il numero visualizzato tramite la variabile limit anziché codificarlo in modo permanente. Ecco gli aggiornamenti da apportare al modello web:

PRIMA:

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

DOPO:

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

In questo modo si concludono le modifiche necessarie per aggiungere l'utilizzo delle attività pull di App Engine Task Queue all'app di esempio del modulo 1. La tua directory ora rappresenta l'app di esempio del modulo 18 e deve contenere questi file:

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

6. Riepilogo/Pulizia

Questa sezione conclude il codelab eseguendo il deployment dell'app, verificando che funzioni come previsto e in qualsiasi output riflesso. Esegui il worker separatamente per elaborare i conteggi dei visitatori. Dopo la convalida dell'app, esegui i passaggi di pulizia e valuta i passaggi successivi.

Esegui il deployment e verifica l'applicazione

Assicurati di aver già configurato la coda pull come abbiamo fatto quasi all'inizio di questo codelab con gcloud app deploy queue.yaml. Se hai completato questa operazione e la tua app di esempio è pronta, esegui il deployment dell'app con gcloud app deploy. L'output dovrebbe essere identico all'app del Modulo 1, tranne per il fatto che ora include una tabella "Principali visitatori" in alto:

b667551dcbab1a09.png

Anche se il frontend web aggiornato mostra i visitatori principali e le visite più recenti, tieni presente che i conteggi dei visitatori non includono questa visita. L'app mostra i conteggi dei visitatori precedenti mentre rilascia un nuovo incremento dell'attività del conteggio di questo visitatore nella coda pull, un'attività in attesa di essere elaborata.

Puoi eseguire l'attività chiamando /log in vari modi:

Ad esempio, se utilizzi curl per inviare una richiesta GET a /log, l'output avrà il seguente aspetto, dato che hai fornito il tuo PROJECT_ID:

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

Il conteggio aggiornato verrà visualizzato alla visita successiva del sito web. È tutto.

Congratulazioni per aver completato questo codelab per l'aggiunta dell'utilizzo del servizio di coda pull di App Engine Task Queue all'app di esempio. Ora è pronto per la migrazione a Cloud Pub/Sub, Cloud NDB e Python 3 nel modulo 19.

Esegui la pulizia

Generale

Se hai finito per il momento, ti consigliamo di disattivare l'app App Engine per evitare addebiti. Tuttavia, se vuoi fare altri test o esperimenti, la piattaforma App Engine ha una quota senza costi e, finché non superi questo livello di utilizzo, non ti verranno addebitati costi. Questo vale per il calcolo, ma potrebbero essere addebitati anche costi per i servizi App Engine pertinenti, quindi consulta la pagina dei prezzi per ulteriori informazioni. Se questa migrazione coinvolge altri servizi cloud, questi vengono fatturati separatamente. In entrambi i casi, se applicabile, consulta la sezione "Specifiche per questo codelab" di seguito.

Per una divulgazione completa, il deployment su una piattaforma di calcolo serverless di Google Cloud come App Engine comporta costi di build e archiviazione minimi. Cloud Build ha una propria quota senza costi, così come Cloud Storage. L'archiviazione di questa immagine utilizza parte della quota. Tuttavia, potresti vivere in una regione che non dispone di un livello senza costi, quindi tieni sotto controllo l'utilizzo dello spazio di archiviazione per ridurre al minimo i potenziali costi. Le "cartelle" Cloud Storage specifiche che devi esaminare includono:

  • 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
  • I link di archiviazione riportati sopra dipendono dalla tua PROJECT_ID e dalla tua *LOC*, ad esempio "us" se la tua app è ospitata negli Stati Uniti.

D'altra parte, se non intendi continuare con questa applicazione o con altri codelab di migrazione correlati e vuoi eliminare tutto completamente, chiudi il progetto.

Specifico per questo codelab

I servizi elencati di seguito sono univoci per questo codelab. Per saperne di più, consulta la documentazione di ogni prodotto:

Passaggi successivi

In questa "migrazione", hai aggiunto l'utilizzo della coda push di Task Queue all'app di esempio del modulo 1, aggiungendo il supporto per il monitoraggio dei visitatori, implementando così l'app di esempio del modulo 18. Nella migrazione successiva, eseguirai l'upgrade delle attività pull di App Engine a Cloud Pub/Sub. A partire dalla fine del 2021, gli utenti non sono più obbligati a eseguire la migrazione a Cloud Pub/Sub durante l'upgrade a Python 3. Scopri di più nella sezione successiva.

Per la migrazione a Cloud Pub/Sub, consulta il codelab del modulo 19. Oltre a queste, ci sono altre migrazioni da considerare, come Cloud Datastore, Cloud Memorystore, Cloud Storage o Cloud Tasks (code push). Esistono anche migrazioni tra prodotti a Cloud Run e Cloud Functions. Tutti i contenuti di Serverless Migration Station (codelab, video, codice sorgente [se disponibile]) sono accessibili nel relativo repository open source.

7. Migrazione a Python 3

Nell'autunno del 2021, il team di App Engine ha esteso il supporto di molti dei servizi integrati ai runtime di seconda generazione (che hanno un runtime di prima generazione). Di conseguenza, non è più necessario eseguire la migrazione dai servizi integrati come App Engine Task Queue ai servizi Cloud o di terze parti autonomi come Cloud Pub/Sub durante il porting dell'app a Python 3. In altre parole, puoi continuare a utilizzare Task Queue nelle app App Engine Python 3 a condizione che adatti il codice per accedere ai servizi in bundle dai runtime di nuova generazione.

Puoi scoprire di più su come eseguire la migrazione dell'utilizzo dei servizi in bundle a Python 3 nel codelab del modulo 17 e nel relativo video. Sebbene questo argomento non rientri nell'ambito del modulo 18, di seguito sono riportate le versioni Python 3 dell'app del modulo 1 di cui è stato eseguito il porting a Python 3 e che utilizzano ancora App Engine NDB. A un certo punto, sarà disponibile anche una versione Python 3 dell'app del modulo 18.

8. Risorse aggiuntive

Di seguito sono elencate risorse aggiuntive per gli sviluppatori che vogliono esplorare ulteriormente questo modulo di migrazione o quelli correlati, nonché i prodotti correlati. Sono inclusi i luoghi in cui fornire feedback su questi contenuti, i link al codice e vari documenti che potresti trovare utili.

Problemi/feedback relativi ai codelab

Se riscontri problemi con questo codelab, cerca prima il tuo problema prima di presentare una segnalazione. Link per cercare e creare nuovi problemi:

Risorse per la migrazione

I link alle cartelle del repository per il Modulo 1 (INIZIO) e il Modulo 18 (FINE) sono riportati nella tabella di seguito. Puoi accedervi anche dal repository per tutte le migrazioni dei codelab di App Engine; clonalo o scarica un file ZIP.

Codelab

Python 2

Python 3

Module 1

code

Codice (non incluso in questo tutorial)

Modulo 18 (questo codelab)

code

N/A

Riferimenti online

Di seguito sono riportate le risorse pertinenti per questo tutorial:

Coda delle attività App Engine

Piattaforma App Engine

Documentazione di App Engine

Runtime Python 2 App Engine (ambiente standard)

Runtime Python 3 App Engine (ambiente standard)

Differenze tra i runtime di Python 2 e 3 di App Engine (ambiente standard)

Guida alla migrazione da Python 2 a 3 di App Engine (ambiente standard)

Informazioni su prezzi e quote di App Engine

Lancio della piattaforma App Engine di seconda generazione (2018)

Supporto a lungo termine per i runtime legacy

Esempi di migrazione della documentazione

Altre informazioni sul cloud

Video

Licenza

Questo lavoro è concesso in licenza ai sensi di una licenza Creative Commons Attribution 2.0 Generic.