Modalità di gestione delle richieste

ID regione

REGION_ID è un codice abbreviato che Google assegna in base alla regione selezionata al momento della creazione dell'app. Il codice non corrisponde a un paese o a una provincia, anche se alcuni ID regione possono sembrare simili ai codici paese e provincia di uso comune. Per le app create dopo febbraio 2020, REGION_ID.r è incluso negli URL di App Engine. Per le app esistenti create prima di questa data, l'ID regione è facoltativo nell'URL.

Scopri di più sugli ID regione.

Questo documento descrive in che modo l'applicazione App Engine riceve richieste e invia risposte.

Per ulteriori dettagli, consulta l'articolo Riferimento a intestazioni e risposte delle richieste.

Se la tua applicazione utilizza servizi, puoi indirizzare le richieste a un servizio specifico o a una versione specifica di quel servizio. Per saperne di più sull'indirizzabilità del servizio, consulta Come vengono instradate le richieste.

Gestione delle richieste

L'applicazione è responsabile dell'avvio di un server web e della gestione delle richieste. Puoi utilizzare qualsiasi framework web disponibile per il linguaggio di sviluppo.

App Engine esegue più istanze dell'applicazione e ciascuna ha il proprio server web per la gestione delle richieste. Qualsiasi richiesta può essere instradata a qualsiasi istanza, quindi le richieste consecutive dello stesso utente non vengono inviate necessariamente alla stessa istanza. Un'istanza può gestire più richieste contemporaneamente. Il numero di istanze può essere regolato automaticamente in base alle variazioni del traffico. Puoi anche modificare il numero di richieste in parallelo che un'istanza può gestire impostando l'elemento max_concurrent_requests nel file app.yaml.

Quando App Engine riceve una richiesta web per la tua applicazione, richiama lo script del gestore corrispondente all'URL, come descritto nel file di configurazione app.yaml dell'applicazione. Il runtime Python 2.7 supporta lo standard WSGI e lo standard CGI per la compatibilità con le versioni precedenti. È preferibile utilizzare WSGI, e alcune funzionalità di Python 2.7 non funzionano senza questo protocollo. La configurazione dei gestori di script della tua applicazione determina se una richiesta viene gestita tramite WSGI o CGI.

Il seguente script Python risponde a una richiesta con un'intestazione HTTP e il messaggio Hello, World!.

import webapp2


class MainPage(webapp2.RequestHandler):
    def get(self):
        self.response.headers["Content-Type"] = "text/plain"
        self.response.write("Hello, World!")


app = webapp2.WSGIApplication(
    [
        ("/", MainPage),
    ],
    debug=True,
)

Per inviare più richieste a ciascun server web in parallelo, contrassegna la tua applicazione come threadsafe aggiungendo un elemento threadsafe: true al file app.yaml. Le richieste in parallelo non sono disponibili se un gestore di script utilizza CGI.

Quote e limiti

App Engine alloca automaticamente le risorse alla tua applicazione man mano che il traffico aumenta. Tuttavia, ciò è vincolato dalle seguenti restrizioni:

  • App Engine riserva la capacità di scalabilità automatica per le applicazioni con bassa latenza, in cui l'applicazione risponde alle richieste in meno di un secondo.

  • Le applicazioni fortemente vincolate alla CPU possono inoltre subire una latenza aggiuntiva per condividere in modo efficiente le risorse con altre applicazioni sugli stessi server. Le richieste di file statici sono esenti da questi limiti di latenza.

Ogni richiesta in arrivo per l'applicazione viene conteggiata ai fini del limite di Richieste. I dati inviati in risposta a una richiesta vengono conteggiati ai fini del limite di Larghezza di banda in uscita (fatturabile).

Sia le richieste HTTP che quelle HTTPS (sicuri) vengono conteggiate ai fini dei limiti per Richieste, Larghezza di banda in entrata (fatturabile) e Larghezza di banda in uscita (fatturabile). Nella pagina dei dettagli delle quote della console Google Cloud vengono anche indicati Richieste sicure, Larghezza di banda in entrata sicura e Larghezza di banda in uscita sicura come valori separati a scopo informativo. Solo le richieste HTTPS vengono conteggiate per questi valori. Per ulteriori informazioni, consulta la pagina Quote.

I seguenti limiti si applicano nello specifico all'uso dei gestori delle richieste:

Limite Quantità
Dimensioni richiesta 32 megabyte
Dimensione della risposta 32 megabyte
Timeout richiesta Dipende dal tipo di scalabilità usata dall'app
Numero totale massimo di file (file di app e file statici) 10.000 in totale
1000 per directory
Dimensione massima del file di un'applicazione 32 megabyte
Dimensione massima di un file statico 32 megabyte
Dimensione totale massima di tutti i file statici e dell'applicazione Il primo gigabyte è gratuito
$ 0,026 per gigabyte al mese dopo il primo gigabyte
Timeout richiesta in attesa 10 secondi
Dimensione massima di un campo di intestazione della richiesta singola 8 kilobyte per runtime di seconda generazione nell'ambiente standard. Le richieste a questi runtime con campi di intestazione che superano gli 8 kilobyte restituiranno errori HTTP 400.

Limiti per le richieste

Tutte le richieste HTTP/2 verranno tradotte in richieste HTTP/1.1 quando inoltrate al server delle applicazioni.

Limiti di risposta

  • Le risposte dinamiche sono limitate a 32 MB. Se un gestore di script genera una risposta superiore a questo limite, il server restituisce una risposta vuota con un codice di stato 500 Errore interno del server. Questo limite non si applica alle risposte che forniscono dati dal Blobstore legacy o da Cloud Storage.

  • Il limite per l'intestazione della risposta è 8 kB per i runtime di seconda generazione. Le intestazioni delle risposte che superano questo limite restituiranno errori HTTP 502, con i log che mostrano upstream sent too big header while reading response header from upstream.

Intestazioni delle richieste

Una richiesta HTTP in entrata include le intestazioni HTTP inviate dal client. Per motivi di sicurezza, alcune intestazioni vengono bonificate o modificate da proxy intermedi prima che raggiungano l'applicazione.

Per ulteriori informazioni, consulta Riferimento alle intestazioni delle richieste.

Gestione dei timeout delle richieste

App Engine è ottimizzato per le applicazioni con richieste di breve durata, in genere quelle che richiedono alcune centinaia di millisecondi. Un'app efficiente risponde rapidamente per la maggior parte delle richieste. Un'app che non scala meglio con l'infrastruttura di App Engine. Per garantire questo livello di prestazioni, esiste un timeout per le richieste massimo imposto dal sistema, tramite il quale ogni app deve rispondere.

Se la tua app supera questa scadenza, App Engine interrompe il gestore di richieste. L'ambiente di runtime Python consente di raggiungere questo obiettivo generando un'eccezione DeadlineExceededError da google.appengine.runtime. Se il gestore delle richieste non rileva questa eccezione, come in tutte le eccezioni non rilevate, l'ambiente di runtime restituisce un errore del server HTTP 500 al client.

Il gestore delle richieste può rilevare questo errore per personalizzare la risposta. L'ambiente di runtime concede al gestore delle richieste un po' più di tempo (meno di un secondo) per preparare una risposta personalizzata dopo aver sollevato l'eccezione.

class TimerHandler(webapp2.RequestHandler):
    def get(self):
        from google.appengine.runtime import DeadlineExceededError

        try:
            time.sleep(70)
            self.response.write("Completed.")
        except DeadlineExceededError:
            self.response.clear()
            self.response.set_status(500)
            self.response.out.write("The request did not complete in time.")

Se il gestore non ha restituito una risposta o non ha sollevato un'eccezione entro la seconda scadenza, viene arrestato e viene restituita una risposta di errore predefinita.

Risposte

App Engine chiama lo script del gestore con Request e attende la restituzione dello script. Tutti i dati scritti nel flusso di output standard vengono inviati come risposta HTTP.

Esistono limiti di dimensioni che si applicano alla risposta generata, che può essere modificata prima di essere restituita al client.

Per ulteriori informazioni, consulta l'articolo Riferimento alle richieste di risposte.

Risposte dinamiche

App Engine non supporta le risposte in modalità flusso in cui i dati vengono inviati in blocchi incrementali al client durante l'elaborazione di una richiesta. Tutti i dati del tuo codice vengono raccolti come descritto in precedenza e inviati come singola risposta HTTP.

Compressione delle risposte

App Engine fa del suo meglio per pubblicare contenuti compressi (gzip) ai client che lo supportano. Per determinare se i contenuti devono essere compressi, App Engine fa quanto segue quando riceve una richiesta:

  1. Conferma se il client può ricevere in modo affidabile risposte compresse visualizzando entrambe le intestazioni Accept-Encoding e User-Agent nella richiesta. Questo approccio evita alcuni bug noti con contenuti compressi gzip nei browser più diffusi.

  2. Per confermare che la compressione dei contenuti è appropriata, visualizza l'intestazione Content-Type che hai configurato per il gestore delle risposte. In generale, la compressione è appropriata per i tipi di contenuti basati su testo e non per i tipi di contenuti binari.

Tieni presente quanto segue:

  • Un client può forzare la compressione dei tipi di contenuti basati su testo impostando entrambe le intestazioni della richiesta Accept-Encoding e User-Agent su gzip.

  • Se una richiesta non specifica gzip nell'intestazione Accept-Encoding, App Engine non comprime i dati della risposta.

  • Google Frontend memorizza nella cache le risposte dei gestori di directory e file statici di App Engine. In base a una serie di fattori, ad esempio il tipo di dati di risposta memorizzati per primi nella cache, le intestazioni Vary specificate nella risposta e quelle incluse nella richiesta, un client potrebbe richiedere dati compressi ma ricevere dati non compressi e viceversa. Per ulteriori informazioni, consulta Memorizzazione nella cache delle risposte.

Memorizzazione nella cache delle risposte

Google Frontend e, potenzialmente, il browser dell'utente e altri server proxy con memorizzazione nella cache intermedia, memorizzano nella cache le risposte dell'app come indicato dalle intestazioni di memorizzazione nella cache standard da te specificate nella risposta. Puoi specificare queste intestazioni delle risposte tramite il tuo framework, direttamente nel codice oppure tramite gestori di file e directory statici di App Engine.

In Google Frontend, la chiave cache è l'URL completo della richiesta.

Memorizzazione nella cache di contenuti statici

Per garantire che i clienti ricevano sempre i contenuti statici aggiornati non appena vengono pubblicati, ti consigliamo di pubblicare contenuti statici da directory con controllo delle versioni, ad esempio css/v1/styles.css. Google Frontend non convaliderà la cache (verifica la presenza di contenuti aggiornati) fino alla scadenza. Anche dopo la scadenza della cache, la cache non viene aggiornata finché i contenuti all'URL della richiesta non vengono modificati.

Le seguenti intestazioni di risposta che puoi impostare in app.yaml influiscono su come e quando Google Frontend memorizza i contenuti nella cache:

  • Cache-Control deve essere impostato su public per consentire al Google Frontend di memorizzare i contenuti nella cache. Potrebbe anche essere memorizzato nella cache dal Google Frontend a meno che non specifichi un'istruzione Cache-Control private o no-store. Se non imposti questa intestazione in app.yaml, App Engine la aggiunge automaticamente per tutte le risposte gestite da un file statico o da un gestore di directory. Per ulteriori informazioni, vedi Intestazioni aggiunte o sostituite.

  • Vary: per consentire alla cache di restituire risposte diverse per un URL in base alle intestazioni inviate nella richiesta, imposta uno o più dei seguenti valori nell'intestazione della risposta Vary: Accept, Accept-Encoding, Origin o X-Origin

    A causa della potenziale cardinalità elevata, i dati non verranno memorizzati nella cache per altri valori di Vary.

    Ad esempio:

    1. Specifica la seguente intestazione della risposta:

      Vary: Accept-Encoding

    2. La tua app riceve una richiesta contenente l'intestazione Accept-Encoding: gzip. App Engine restituisce una risposta compressa e Google Frontend memorizza nella cache la versione compressa in gzip dei dati della risposta. Tutte le successive richieste per questo URL che contengono l'intestazione Accept-Encoding: gzip riceveranno i dati compressi in gzip dalla cache fino all'annullamento della validità della cache (a causa della modifica dei contenuti dopo la scadenza della cache).

    3. La tua app riceve una richiesta che non contiene l'intestazione Accept-Encoding. App Engine restituisce una risposta non compressa e Google Frontend memorizza nella cache la versione non compressa dei dati della risposta. Tutte le successive richieste per questo URL che non contengono l'intestazione Accept-Encoding riceveranno i dati compressi dalla cache finché la cache non viene invalidata.

    Se non specifichi un'intestazione della risposta Vary, Google Frontend crea una singola voce della cache per l'URL e la utilizzerà per tutte le richieste, indipendentemente dalle intestazioni della richiesta. Ad esempio:

    1. Non devi specificare l'intestazione della risposta Vary: Accept-Encoding.
    2. Una richiesta contiene l'intestazione Accept-Encoding: gzip e la versione compressa con gzip dei dati della risposta verrà memorizzata nella cache.
    3. Una seconda richiesta non contiene l'intestazione Accept-Encoding: gzip. Tuttavia, poiché la cache contiene una versione compressa con gzip dei dati della risposta, la risposta verrà compressa in formato gzip anche se il client ha richiesto dati non compressi.

Anche le intestazioni della richiesta influiscono sulla memorizzazione nella cache:

  • Se la richiesta contiene un'intestazione Authorization, i contenuti non verranno memorizzati nella cache da Google Frontend.

Scadenza cache

Per impostazione predefinita, le intestazioni di memorizzazione nella cache che i gestori di file e directory di App Engine aggiungono alle risposte indicano ai client e ai proxy web come Google Frontend di scadere la cache dopo 10 minuti.

Dopo che un file viene trasmesso con una determinata data di scadenza, in genere non c'è modo di svuotarlo dalle cache dei proxy web, anche se l'utente svuota la cache del proprio browser. Il nuovo deployment di una nuova versione dell'app non reimposterà le cache. Pertanto, se prevedi di modificare un file statico, la scadenza deve essere rapida (meno di un'ora). Nella maggior parte dei casi, la scadenza predefinita di 10 minuti è appropriata.

Puoi modificare la scadenza predefinita per tutti i gestori di file e directory statici specificando l'elemento default_expiration nel file app.yaml. Per impostare tempi di scadenza specifici per i singoli gestori, specifica l'elemento expiration all'interno dell'elemento gestore nel file app.yaml.

Il valore specificato nella durata degli elementi di scadenza verrà utilizzato per impostare le intestazioni delle risposte HTTP Cache-Control e Expires.

Memorizzazione nella cache dell'app

L'ambiente di runtime Python memorizza nella cache i moduli importati tra le richieste su un singolo server web, in modo simile al modo in cui un'applicazione Python autonoma carica un modulo solo una volta anche se il modulo è importato da più file. Poiché i gestori WSGI sono moduli, vengono memorizzati nella cache tra le richieste. Gli script del gestore CGI vengono memorizzati nella cache solo se forniscono una routine main(); in caso contrario, lo script del gestore CGI viene caricato per ogni richiesta.

La memorizzazione nella cache delle app offre un vantaggio significativo in termini di tempo di risposta. Consigliamo a tutti gli script dei gestori CGI di usare una routine main(), come descritto di seguito.

Le importazioni vengono memorizzate nella cache

Per maggiore efficienza, il server web mantiene i moduli importati in memoria e non li ricarica o li valuta nelle richieste successive alla stessa applicazione sullo stesso server. La maggior parte dei moduli non inizializza dati globali né ha altri effetti collaterali quando viene importata, quindi la memorizzazione nella cache non cambia il comportamento dell'applicazione.

Se l'applicazione importa un modulo che dipende dal modulo valutato per ogni richiesta, l'applicazione deve soddisfare questo comportamento di memorizzazione nella cache.

Memorizzazione nella cache dei gestori CGI

Puoi indicare ad App Engine di memorizzare nella cache lo script del gestore CGI stesso, oltre ai moduli importati. Se lo script del gestore definisce una funzione denominata main(), lo script e il relativo ambiente globale verranno memorizzati nella cache come un modulo importato. La prima richiesta dello script su un determinato server web valuta normalmente lo script. Per le richieste successive, App Engine chiama la funzione main() nell'ambiente memorizzato nella cache.

Per memorizzare nella cache uno script del gestore, App Engine deve essere in grado di chiamare main() senza argomenti. Se lo script del gestore non definisce una funzione main() o se la funzione main() richiede argomenti (che non hanno valori predefiniti), App Engine carica e valuta l'intero script per ogni richiesta.

Conservare il codice Python analizzato consente di risparmiare tempo e di ottenere risposte più rapide. La memorizzazione nella cache dell'ambiente globale ha anche altri potenziali usi:

  • Espressioni regolari compilate. Tutte le espressioni regolari vengono analizzate e archiviate in un modulo compilato. Puoi archiviare espressioni regolari compilate in variabili globali, quindi utilizzare la memorizzazione nella cache dell'app per riutilizzare gli oggetti compilati tra le richieste.

  • GqlQuery. La stringa di query GQL viene analizzata al momento della creazione dell'oggetto GqlQuery. Riutilizzare un oggetto GqlQuery con associazione di parametri e il metodo bind() è più veloce rispetto a ricostruire l'oggetto ogni volta. Puoi archiviare un oggetto GqlQuery con associazione di parametri per i valori in una variabile globale e riutilizzarlo associando nuovi valori parametro per ogni richiesta.

  • File di configurazione e di dati. Se l'applicazione carica e analizza i dati di configurazione da un file, può conservare i dati analizzati in memoria per evitare di dover ricaricare il file per ogni richiesta.

Lo script del gestore deve chiamare main() al momento dell'importazione. App Engine prevede che l'importazione dello script chiami main(), quindi App Engine non lo chiama quando carica il gestore di richieste per la prima volta su un server.

La memorizzazione nella cache dell'app con main() fornisce un miglioramento significativo nel tempo di risposta del gestore CGI. Lo consigliamo per tutte le applicazioni che utilizzano CGI.

Logging

Il server web di App Engine acquisisce tutto ciò che lo script del gestore scrive nel flusso di output standard per la risposta alla richiesta web. Inoltre, acquisisce tutto ciò che lo script del gestore scrive nel flusso di errori standard e lo archivia come dati di log. A ogni richiesta viene assegnato un request_id, un identificatore univoco globale basato sull'ora di inizio della richiesta. I dati di log per la tua applicazione possono essere visualizzati nella console Google Cloud utilizzando Cloud Logging.

L'ambiente di runtime Python di App Engine include un supporto speciale per il modulo di logging dalla libreria standard Python al fine di comprendere i concetti di log, come i livelli di log ("debug", "info", "warning", "error", "critical").

import logging

import webapp2


class MainPage(webapp2.RequestHandler):
    def get(self):
        logging.debug("This is a debug message")
        logging.info("This is an info message")
        logging.warning("This is a warning message")
        logging.error("This is an error message")
        logging.critical("This is a critical message")

        try:
            raise ValueError("This is a sample value error.")
        except ValueError:
            logging.exception("A example exception log.")

        self.response.out.write("Logging example.")


app = webapp2.WSGIApplication([("/", MainPage)], debug=True)

Ambiente

L'ambiente di esecuzione imposta automaticamente diverse variabili di ambiente. Puoi impostarne altre in app.yaml. Tra le variabili impostate automaticamente, alcune sono speciali per App Engine, mentre altre fanno parte degli standard WSGI o CGI. Il codice Python può accedere a queste variabili utilizzando il dizionario os.environ.

Le seguenti variabili di ambiente sono specifiche di App Engine:

  • CURRENT_VERSION_ID: la versione principale e secondaria dell'applicazione attualmente in esecuzione, come "X.Y". Il numero di versione principale ("X") è specificato nel file app.yaml dell'app. Il numero di versione secondario ("Y") viene impostato automaticamente quando ogni versione dell'app viene caricata in App Engine. Sul server web di sviluppo, la versione secondaria è sempre "1".

  • AUTH_DOMAIN: il dominio utilizzato per autenticare gli utenti con l'API Users. Le app ospitate su appspot.com hanno un valore AUTH_DOMAIN di gmail.com e accettano qualsiasi Account Google. Le app ospitate su un dominio personalizzato hanno un valore AUTH_DOMAIN uguale a quello del dominio personalizzato.

  • INSTANCE_ID: contiene l'ID istanza dell'istanza di frontend che gestisce una richiesta. L'ID è una stringa esadecimale (ad esempio, 00c61b117c7f7fd0ce9e1325a04b8f0df30deaaf). Un amministratore che ha eseguito l'accesso può utilizzare l'ID in un URL: https://INSTANCE_ID-dot-VERSION_ID-dot-SERVICE_ID-dot-PROJECT_ID.REGION_ID.r.appspot.com. La richiesta verrà instradata a quella specifica istanza di frontend. Se l'istanza non riesce a gestire la richiesta, restituisce immediatamente un errore 503.

Le seguenti variabili di ambiente fanno parte degli standard WSGI e CGI, con un comportamento speciale in App Engine:

  • SERVER_SOFTWARE: nel server web di sviluppo, questo valore è "Development/X.Y", dove "X.Y" è la versione del runtime. Quando viene eseguito su App Engine, questo valore è "Google App Engine/X.Y.Z".

Ulteriori variabili di ambiente vengono impostate secondo lo standard WSGI o CGI. Per ulteriori informazioni su queste variabili, consulta lo standard WSGI o lo standard CGI, a seconda dei casi.

Puoi anche impostare le variabili di ambiente nel file app.yaml:

env_variables:
  DJANGO_SETTINGS_MODULE: 'myapp.settings'

Il seguente gestore di richieste webapp2 mostra ogni variabile di ambiente visibile all'applicazione nel browser:

class PrintEnvironmentHandler(webapp2.RequestHandler):
    def get(self):
        self.response.headers["Content-Type"] = "text/plain"
        for key, value in os.environ.iteritems():
            self.response.out.write("{} = {}\n".format(key, value))

ID richieste

Al momento della richiesta, puoi salvare l'ID richiesta, che è univoco. L'ID richiesta può essere utilizzato in un secondo momento per cercare i log per quella richiesta in Cloud Logging.

Il seguente codice campione mostra come ottenere l'ID richiesta nel contesto di una richiesta:

class RequestIdHandler(webapp2.RequestHandler):
    def get(self):
        self.response.headers["Content-Type"] = "text/plain"
        request_id = os.environ.get("REQUEST_LOG_ID")
        self.response.write("REQUEST_LOG_ID={}".format(request_id))

Forzare le connessioni HTTPS

Per motivi di sicurezza, tutte le applicazioni devono incoraggiare i client a connettersi tramite https. Per indicare al browser di preferire https rispetto a http per una determinata pagina o per l'intero dominio, imposta l'intestazione Strict-Transport-Security nelle risposte. Ad esempio:

Strict-Transport-Security: max-age=31536000; includeSubDomains
Per impostare questa intestazione per eventuali contenuti statici pubblicati dall'app, aggiungi l'intestazione ai gestori di file e directory dell'app.

Per impostare questa intestazione per le risposte generate dal codice, utilizza la libreria flask-talisman.

Gestione asincroni in background

Per lavoro in background si intende qualsiasi attività eseguita dalla tua app per una richiesta dopo che hai inviato la risposta HTTP. Evita di eseguire operazioni in background nell'app ed rivedi il codice per assicurarti che tutte le operazioni asincrone vengano completate prima di inviare la risposta.

Per i job a lunga esecuzione, consigliamo di utilizzare Cloud Tasks. Con Cloud Tasks, le richieste HTTP hanno una lunga durata e restituiscono una risposta solo al termine del lavoro asincrono.