Modalità di gestione delle richieste

ID regione

REGION_ID è un codice abbreviato assegnato da Google in base alla regione selezionata quando crei l'app. Il codice non corrisponde a un paese o a una provincia, anche se alcuni ID regione possono sembrare simili ai codici di paesi e province 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 come l'applicazione App Engine riceve le richieste e invia le risposte.

Per maggiori dettagli, consulta la documentazione di riferimento su intestazioni di richiesta e risposte.

Se la tua applicazione utilizza i servizi, puoi indirizzare le richieste a un servizio specifico o a una versione specifica di quel servizio. Per ulteriori informazioni sull'addressability del servizio, consulta In che modo 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 tuo linguaggio di sviluppo.

App Engine esegue più istanze della tua applicazione e ogni istanza ha il proprio server web per gestire le richieste. Qualsiasi richiesta può essere indirizzata a qualsiasi istanza, pertanto le richieste consecutive dello stesso utente non vengono necessariamente inviate 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 simultanee 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, chiama lo script di gestore corrispondente all'URL, come descritto nel file di configurazione app.yaml dell'applicazione. Il runtime di Python 2.7 supporta lo standard WSGI e lo standard CGI per la compatibilità con le versioni precedenti. WSGI è la scelta preferita e alcune funzionalità di Python 2.7 non funzionano senza. La configurazione degli handler script della tua applicazione determina se una richiesta viene gestita utilizzando 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 sicura per i thread aggiungendo un threadsafe: true al file app.yaml. Le richieste simultanee 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, sono previste le seguenti limitazioni:

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

  • Le applicazioni che richiedono un'elevata elaborazione della CPU potrebbero anche presentare 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 all'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 (sicure) vengono conteggiate ai fini dei limiti di Richieste, Larghezza di banda in entrata (fatturabile) e Larghezza di banda in uscita (fatturabile). La pagina dei dettagli delle quote della console Google Cloud riporta anche 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 specificamente all'utilizzo dei gestori delle richieste:

Limite Quantità
Dimensioni richiesta 32 megabyte
Dimensione della risposta 32 megabyte
Timeout richiesta Dipende dal tipo di scalabilità utilizzato dalla tua app
Numero totale massimo di file (file dell'app e file statici) 10.000 in totale
1000 per directory
Dimensione massima di un file dell'applicazione 32 megabyte
Dimensione massima di un file statico 32 megabyte
Dimensioni totali massime di tutti i file di applicazioni e statici I primi 1 GB sono gratuiti
0,026$ per GB al mese dopo i primi 1 GB
Timeout della richiesta in attesa 10 secondi
Dimensione massima di un singolo campo dell'intestazione della richiesta 8 kilobyte per i runtime di seconda generazione nell'ambiente standard. Le richieste a questi runtime con campi di intestazione superiori a 8 kilobyte restituiranno errori HTTP 400.

Limiti per le richieste

Tutte le richieste HTTP/2 verranno tradotte in richieste HTTP/1.1 quando vengono inoltrate al server dell'applicazione.

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). Questa limitazione non si applica alle risposte che forniscono dati provenienti dall'archivio BLOB precedente o da Cloud Storage.

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

Intestazioni delle richieste

Una richiesta HTTP in arrivo include le intestazioni HTTP inviate dal client. Per motivi di sicurezza, alcune intestazioni vengono sottoposte a sanificazione o modificate da proxy intermedi prima di raggiungere l'applicazione.

Per ulteriori informazioni, consulta la sezione Informazioni di riferimento sulle intestazioni delle richieste.

Gestione dei timeout delle richieste

App Engine è ottimizzato per le applicazioni con richieste di breve durata, tipicamente quelle che richiedono alcune centinaia di millisecondi. Un'app efficiente risponde rapidamente alla maggior parte delle richieste. Un'app che non lo fa non avrà una buona scalabilità con l'infrastruttura di App Engine. Per garantire questo livello di prestazioni, esiste un timeout della richiesta massimo imposto dal sistema entro il quale ogni app deve rispondere.

Se la tua app supera questa scadenza, App Engine interrompe il gestore delle richieste. L'ambiente di runtime Python lo fa sollevando un'eccezione DeadlineExceededError da google.appengine.runtime. Se il gestore di richieste non intercetta questa eccezione, come per tutte le eccezioni non rilevate, l'ambiente di runtime restituirà al client un errore del server HTTP 500.

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

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, il gestore viene interrotto e viene restituita una risposta di errore predefinita.

Risposte

App Engine chiama lo script di gestore con un Request e attende il suo ritorno. Tutti i dati scritti nello stream di output standard vengono inviati come risposta HTTP.

Esistono limiti di dimensione che si applicano alla risposta generata e la risposta può essere modificata prima di essere restituita al client.

Per ulteriori informazioni, consulta la sezione Documentazione di riferimento per le risposte alle richieste.

Risposte dinamiche

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

Compressione delle risposte

App Engine fa del suo meglio per pubblicare contenuti compressi (con gzip) per i client che li supportano. Per determinare se i contenuti devono essere compressi, App Engine esegue le seguenti operazioni quando riceve una richiesta:

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

  2. Verifica se la compressione dei contenuti è appropriata visualizzando l'intestazione Content-Type che hai configurato per il gestore della risposta. 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 di richiesta Accept-Encoding e User-Agent su gzip.

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

  • Il Frontend di Google memorizza nella cache le risposte dei gestori di directory e file statici di App Engine. A seconda di una serie di fattori, ad esempio il tipo di dati della risposta memorizzati nella cache per primo, le intestazioni Vary che hai specificato nella risposta e le intestazioni incluse nella richiesta, un client potrebbe richiedere dati compressi, ma ricevere dati non compressi e viceversa. Per maggiori informazioni, consulta Memorizzazione nella cache delle risposte.

Memorizzazione nella cache delle risposte

Il frontend di Google e potenzialmente il browser dell'utente e altri server proxy intermediari per la memorizzazione nella cache memorizzeranno nella cache le risposte della tua app come indicato dalle intestazioni di memorizzazione nella cache standard specificate nella risposta. Puoi specificare queste intestazioni di risposta tramite il tuo framework, direttamente nel codice o tramite gli handler di directory e file statici di App Engine.

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

Memorizzazione nella cache dei contenuti statici

Per assicurarti che i clienti ricevano sempre contenuti statici aggiornati non appena vengono pubblicati, ti consigliamo di pubblicare contenuti statici da directory con versioni, ad esempio css/v1/styles.css. Il Frontend di Google non convalida la cache (controlla la presenza di contenuti aggiornati) fino alla scadenza della cache. Anche dopo la scadenza della cache, questa non verrà aggiornata finché i contenuti dell'URL della richiesta non cambieranno.

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

  • Cache-Control deve essere impostato su public affinché il frontend di Google memorizzi nella cache i contenuti. Potrebbe anche essere memorizzato nella cache dal frontend di Google, a meno che non specifichi un'istruzione Cache-Control private o no-store. Se non imposti questo header in app.yaml , App Engine lo aggiunge automaticamente per tutte le risposte gestite da un gestore di directory o file statico. Per ulteriori informazioni, consulta la sezione Intestazioni aggiunte o sostituite.

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

    A causa della potenziale alta cardinalità, i dati non verranno memorizzati nella cache per altri valoriVary.

    Ad esempio:

    1. Specifica la seguente intestazione di risposta:

      Vary: Accept-Encoding

    2. La tua app riceve una richiesta che contiene l'intestazione Accept-Encoding: gzip. App Engine restituisce una risposta compressa e il frontend di Google memorizza nella cache la versione compressa con gzip dei dati della risposta. Tutte le richieste successive per questo URL che contengono l'intestazione Accept-Encoding: gzip riceveranno i dati compressi con gzip dalla cache fino a quando la cache non viene invalidata (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 richieste successive per questo URL che non contengono l'intestazione Accept-Encoding riceveranno i dati compressi dalla cache finché la cache non diventa non valida.

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

    1. Non specifichi l'intestazione della risposta Vary: Accept-Encoding.
    2. Una richiesta contiene l'intestazione Accept-Encoding: gzip e la versione compressa con gzip dei dati di 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 di risposta, la risposta verrà compressa con gzip anche se il client ha richiesto dati non compressi.

Le intestazioni nella richiesta influiscono anche sulla memorizzazione nella cache:

  • Se la richiesta contiene un'intestazione Authorization, i contenuti non verranno memorizzati nella cache dal frontend di Google.

Scadenza della cache

Per impostazione predefinita, le intestazioni di memorizzazione nella cache aggiunte ai gestori di directory e file statici di App Engine alle risposte ordinano ai client e ai proxy web, come il frontend di Google, di far scadere la cache dopo 10 minuti.

Dopo che un file è stato trasmesso con una determinata data e ora di scadenza, in genere non è possibile rimuoverlo dalle cache del proxy web, anche se l'utente svuota la cache del proprio browser. Il nuovo deployment di una nuova versione dell'app non reimposta le cache. Pertanto, se prevedi di modificare un file statico, deve avere un breve periodo di scadenza (meno di un'ora). Nella maggior parte dei casi, il valore predefinito di 10 minuti per la data e l'ora di scadenza è appropriato.

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 date di scadenza specifiche per i singoli gestori, specifica l'elemento expiration all'interno dell'elemento gestore nel file app.yaml.

Il valore specificato nell'elemento di scadenza verrà utilizzato per impostare le intestazioni di risposta HTTP Cache-Control e Expires.

Memorizzazione nella cache delle app

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

La memorizzazione nella cache dell'app offre un vantaggio significativo in termini di tempo di risposta. Ti consigliamo di utilizzare una routine main() per tutti gli script di gestore CGI, come descritto di seguito.

Le importazioni vengono memorizzate nella cache

Per motivi di efficienza, il server web mantiene in memoria i moduli importati e non li carica di nuovo o li valuta di nuovo nelle richieste successive alla stessa applicazione sullo stesso server. La maggior parte dei moduli non inizializza dati globali o non ha altri effetti collaterali quando vengono importati, pertanto la loro memorizzazione nella cache non modifica il comportamento dell'applicazione.

Se la tua applicazione importa un modulo che dipende dal modulo valutato per ogni richiesta, deve supportare questo comportamento di memorizzazione nella cache.

Gestori CGI con memorizzazione nella cache

Puoi chiedere ad App Engine di memorizzare nella cache lo script dell'handler CGI stesso, oltre ai moduli importati. Se lo script dell'handler 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 lo valuta normalmente. Per le richieste successive, App Engine chiama la funzione main() nell'ambiente memorizzato nella cache.

Per memorizzare nella cache uno script di gestore, App Engine deve essere in grado di chiamare main() senza argomenti. Se lo script di 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.

Mantenere in memoria 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 utilizzi:

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

  • Oggetti GqlQuery. La stringa di query GQL viene analizzata quando viene creato l'oggetto GqlQuery. Riutilizzare un oggetto GqlQuery con il binding dei parametri e il metodo bind() è più veloce che ricostruire l'oggetto ogni volta. Puoi memorizzare un oggetto GqlQuery con il binding dei parametri per i valori in una variabile globale, quindi riutilizzarlo associando nuovi valori dei parametri per ogni richiesta.

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

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

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

Logging

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

L'ambiente runtime Python di App Engine include un supporto speciale per il modulo di logging della libreria standard di Python per comprendere i concetti di logging, 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)

L'ambiente

L'ambiente di esecuzione imposta automaticamente diverse variabili di ambiente. Puoi impostarne altre in app.yaml. Delle variabili impostate automaticamente, alcune sono specifiche 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: le versioni principali e secondarie dell'applicazione in esecuzione, come "X.Y". Il numero di versione principale ("X") è specificato nel file app.yaml dell'app. Il numero della versione secondaria ("Y") viene impostato automaticamente quando ogni versione dell'app viene caricata su App Engine. Sul web server di sviluppo, la versione minore è sempre "1".

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

  • INSTANCE_ID: contiene l'ID istanza dell'istanza di frontend che gestisce una richiesta. L'ID è una stringa esadecimale (ad es. 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à indirizzata a quell'istanza frontend specifica. Se l'istanza non è in grado di gestire la richiesta, restituisce immediatamente un codice 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 è "Sviluppo/X.Y", dove "X.Y" è la versione del runtime. Quando viene eseguito su App Engine, questo valore è "Google App Engine/X.Y.Z".

Le variabili di ambiente aggiuntive vengono impostate in base allo 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 app.yaml file:

env_variables:
  DJANGO_SETTINGS_MODULE: 'myapp.settings'

Il seguente gestore delle 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 richiesta

Al momento della richiesta, puoi salvare l'ID richiesta, che è univoco per quella richiesta. L'ID richiesta può essere utilizzato in un secondo momento per cercare i log relativi alla 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 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 questo intestazione per tutti i contenuti statici pubblicati dalla tua app, aggiungila agli handler di file e directory statici della tua app.

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

Gestione del lavoro in background asincrono

Il lavoro in background è qualsiasi lavoro eseguito dalla tua app per una richiesta dopo che hai fornito la risposta HTTP. Evita di eseguire operazioni in background nella tua app e controlla il codice per assicurarti che tutte le operazioni asincrone vengano completate prima di inviare la risposta.

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