Modalità di gestione delle richieste

ID regione

Il REGION_ID è un codice abbreviato che Google assegna in base alla regione selezionata durante la creazione dell'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 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 guida di riferimento per 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 ulteriori informazioni sull'indirizzabilità del servizio, vedi Come vengono instradate le richieste.

Gestione delle richieste

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

Quando App Engine riceve una richiesta web per la tua applicazione, richiama la servlet corrispondente all'URL, come descritto nel file web.xml dell'applicazione nella directory WEB-INF/. Supporta le specifiche dell'API Java Servlet 2.5 o 3.1, per fornire i dati della richiesta alla servlet e accettare i dati della risposta.

App Engine esegue più istanze dell'applicazione e ogni istanza ha il proprio web server per la gestione delle richieste. Qualsiasi richiesta può essere indirizzata a qualsiasi istanza, quindi le richieste consecutive dello stesso utente non vengono necessariamente inviate alla stessa istanza. Il numero di istanze può essere modificato automaticamente in base alle variazioni del traffico.

Per impostazione predefinita, ogni server web elabora una sola richiesta alla volta. Per inviare più richieste a ogni web server in parallelo, contrassegna la tua applicazione come thread-safe aggiungendo un elemento <threadsafe>true</threadsafe> al file appengine-web.xml.

La seguente classe servlet di esempio mostra un semplice messaggio nel browser dell'utente.

// With @WebServlet annotation the webapp/WEB-INF/web.xml is no longer required.
@WebServlet(name = "requests", description = "Requests: Trivial request", urlPatterns = "/requests")
public class RequestsServlet extends HttpServlet {

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    resp.setContentType("text/plain");
    resp.getWriter().println("Hello, world");
  }
}

Quote e limiti

App Engine alloca automaticamente le risorse alla tua applicazione man mano che il traffico aumenta. Tuttavia, questa operazione è vincolata alle seguenti limitazioni:

  • 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 con un elevato utilizzo della CPU potrebbero anche comportare 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 entrata 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).

Le richieste HTTP e HTTPS (sicure) vengono conteggiate ai fini dei limiti di Richieste, Larghezza di banda in entrata (fatturabile) e Larghezza di banda in uscita (fatturabile). La Google Cloud console pagina Dettagli quota riporta anche Richieste sicure, Larghezza di banda in entrata sicura e Larghezza di banda in uscita sicura come valori separati a scopo informativo. Ai fini di questi valori, vengono conteggiate solo le richieste HTTPS. 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 totali
1000 per directory
Dimensione massima di un file dell'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 della richiesta in attesa 10 secondi
Dimensione massima di un singolo campo di 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 convertite in richieste HTTP/1.1 quando vengono 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 Internal Server Error. Questa limitazione non si applica alle risposte che forniscono dati da Blobstore legacy o Cloud Storage.

  • Il limite dell'intestazione della risposta è di 8 KB per gli 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 entrata include le intestazioni HTTP inviate dal client. Per motivi di sicurezza, alcune intestazioni vengono sanificate o modificate da proxy intermedi prima di raggiungere l'applicazione.

Per maggiori informazioni, consulta la documentazione di riferimento delle intestazioni delle richieste.

Gestione dei timeout delle richieste

App Engine è ottimizzato per le applicazioni con richieste di breve durata, in genere quelle che richiedono poche centinaia di millisecondi. Un'app efficiente risponde rapidamente alla maggior parte delle richieste. Un'app che non lo fa non verrà scalata correttamente con l'infrastruttura di App Engine. Per garantire questo livello di prestazioni, esiste un timeout della richiesta massimo imposto dal sistema a cui ogni app deve rispondere.

Se la tua app supera questa scadenza, App Engine interrompe il gestore di richieste. L'ambiente di runtime Java interrompe la servlet generando un'eccezione com.google.apphosting.api.DeadlineExceededException. Se non è presente un gestore di richieste per rilevare questa eccezione, l'ambiente di runtime restituirà al client un errore del server HTTP 500.

Se è presente un gestore delle richieste e viene rilevato DeadlineExceededException, l'ambiente di runtime concede al gestore delle richieste un tempo (inferiore a un secondo) per preparare una risposta personalizzata. Se il gestore delle richieste impiega più di un secondo dopo aver generato l'eccezione per preparare una risposta personalizzata, verrà generato un HardDeadlineExceededError.

Sia DeadlineExceededExceptions che HardDeadlineExceededErrors forzeranno l'interruzione della richiesta e l'arresto dell'istanza.

Per sapere quanto tempo rimane prima della scadenza, l'applicazione può importare com.google.apphosting.api.ApiProxy e chiamare ApiProxy.getCurrentEnvironment().getRemainingMillis(). È utile se l'applicazione prevede di iniziare un lavoro che potrebbe richiedere troppo tempo. Se sai che l'elaborazione di un'unità di lavoro richiede cinque secondi, ma getRemainingMillis() restituisce meno tempo, non ha senso iniziare quell'unità di lavoro.

Risposte

App Engine chiama il servlet con un oggetto richiesta e un oggetto risposta, quindi attende che il servlet compili l'oggetto risposta e lo restituisca. Quando la servlet viene restituita, i dati nell'oggetto risposta vengono inviati all'utente.

Esistono limiti di dimensione che si applicano alla risposta che generi e la risposta potrebbe essere modificata prima di essere restituita al client.

Per maggiori informazioni, consulta la documentazione di riferimento sulle risposte alle richieste.

Risposte dinamiche

App Engine non supporta le risposte in streaming 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 sopra e inviati come una singola risposta HTTP.

Compressione delle risposte

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

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

  2. Conferma se la compressione dei contenuti è appropriata visualizzando l'intestazione Content-Type che hai configurato per l'handler di risposta. In generale, la compressione è adatta ai tipi di contenuti basati su testo e non ai 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 comprimerà i dati di risposta.

  • Il frontend di Google memorizza nella cache le risposte dei gestori di file statici e delle directory di App Engine. A seconda di una serie di fattori, ad esempio il tipo di dati di risposta memorizzati nella cache per primi, le intestazioni Vary specificate 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 proxy server di memorizzazione nella cache intermedi memorizzeranno nella cache le risposte della tua app come indicato dalle intestazioni di memorizzazione nella cache standard che specifichi nella risposta. Puoi specificare queste intestazioni di risposta tramite il framework, direttamente nel codice o tramite i gestori di file e directory statici di App Engine.

Nel frontend di Google, la chiave della cache è l'URL completo della richiesta.

Memorizzazione nella cache dei contenuti statici

Per garantire che i client ricevano sempre 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. Il frontend di Google non convaliderà la cache (non verificherà la presenza di contenuti aggiornati) finché non scade. Anche dopo la scadenza della cache, questa non verrà aggiornata finché i contenuti all'URL della richiesta non cambiano.

Le seguenti intestazioni di risposta che puoi impostare in appengine-web.xml influenzano 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; questi potrebbero anche essere memorizzati nella cache dal frontend di Google, a meno che tu non specifichi un'istruzione Cache-Control private o no-store. Se non imposti questa intestazione in appengine-web.xml, App Engine la aggiunge automaticamente a tutte le risposte gestite da un gestore di file o directory statici. Per ulteriori informazioni, consulta la sezione 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 che contiene l'intestazione Accept-Encoding: gzip. App Engine restituisce una risposta compressa e Google Frontend memorizza nella cache la versione compressa dei dati di risposta. Tutte le richieste successive per questo URL che contengono l'intestazione Accept-Encoding: gzip riceveranno i dati compressi con gzip dalla cache finché 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 di 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 utilizzerà per tutte le richieste indipendentemente dalle intestazioni della 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 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.

Anche le intestazioni della richiesta influiscono sulla memorizzazione nella cache:

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

Scadenza della cache

Per impostazione predefinita, le intestazioni di memorizzazione nella cache che i gestori di file statici 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.

Una volta trasmesso un file con un determinato tempo di scadenza, in genere non è possibile cancellarlo dalle cache dei proxy web, anche se l'utente svuota la propria cache del browser. Il redeployment di una nuova versione dell'app non reimposterà le cache. Pertanto, se prevedi di modificare un file statico, questo deve avere un tempo di scadenza breve (inferiore a un'ora). Nella maggior parte dei casi, il tempo di scadenza predefinito di 10 minuti è appropriato.

Puoi modificare la scadenza predefinita per tutti i gestori di file e directory statici specificando l'elemento static-files nel file appengine-web.xml.

Logging

La tua applicazione può scrivere informazioni nei log dell'applicazione utilizzando java.util.logging.Logger. I dati di log per la tua applicazione possono essere visualizzati nella console Google Cloud utilizzando Cloud Logging. A ogni richiesta registrata viene assegnato un ID richiesta, un identificatore univoco globale basato sull'ora di inizio della richiesta. La consoleGoogle Cloud può riconoscere i livelli di log della classe Logger e visualizzare in modo interattivo i messaggi a livelli diversi.

Tutto ciò che la servlet scrive nel flusso di output standard (System.out) e nel flusso di errori standard (System.err) viene acquisito da App Engine e registrato nei log dell'applicazione. Le righe scritte nel flusso di output standard vengono registrate a livello "INFO", mentre le righe scritte nel flusso di errori standard vengono registrate a livello "WARNING". Funzionerà qualsiasi framework di logging (ad esempio log4j) che registra i dati nei flussi di output o di errore. Tuttavia, per un controllo più preciso della visualizzazione del livello di log nella console Google Cloud , il framework di logging deve utilizzare un adattatore java.util.logging.

// With @WebServlet annotation the webapp/WEB-INF/web.xml is no longer required.
@WebServlet(
    name = "RequestLogging",
    description = "Requests: Logging example",
    urlPatterns = "/requests/log"
)
public class LoggingServlet extends HttpServlet {

  private static final Logger log = Logger.getLogger(LoggingServlet.class.getName());

  @Override
  public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    log.info("An informational message.");
    log.warning("A warning message.");
    log.severe("An error message.");
    // ...
  }
}

L'SDK Java di App Engine include un file di modello logging.properties, nella directory appengine-java-sdk/config/user/. Per utilizzarlo, copia il file nella directory WEB-INF/classes (o altrove nel WAR), quindi la proprietà di sistema java.util.logging.config.file in "WEB-INF/logging.properties" (o nel percorso che preferisci, relativo alla radice dell'applicazione). Puoi impostare le proprietà di sistema nel file appengine-web.xml come segue:

<appengine-web-app xmlns="http://appengine.google.com/ns/1.0"> ... <system-properties> <property name="java.util.logging.config.file" value="WEB-INF/logging.properties" /> </system-properties> </appengine-web-app>

La servlet registra i messaggi utilizzando il livello di log INFO (utilizzando log.info()). Il livello di log predefinito è WARNING, che elimina i messaggi INFO dall'output. Per modificare il livello di log, modifica il file logging.properties.

L'ambiente

Tutte le proprietà di sistema e le variabili di ambiente sono private per la tua applicazione. L'impostazione di una proprietà di sistema influisce solo sulla visualizzazione di questa proprietà da parte dell'applicazione, e non sulla visualizzazione della JVM.

Puoi impostare le proprietà di sistema e le variabili di ambiente per la tua app nel descrittore di deployment.

App Engine imposta diverse proprietà di sistema che identificano l'ambiente di runtime:

  • com.google.appengine.runtime.environment è "Production" quando viene eseguito su App Engine e "Development" quando viene eseguito nel server di sviluppo.

    Oltre a utilizzare System.getProperty(), puoi accedere alle proprietà di sistema utilizzando la nostra API type-safe. Ad esempio:

    if (SystemProperty.environment.value() ==
        SystemProperty.Environment.Value.Production) {
        // The app is running on App Engine...
    }
    
  • com.google.appengine.runtime.version è l'ID versione dell'ambiente di runtime, ad esempio "1.3.0". Puoi ottenere la versione richiamando quanto segue: String version = SystemProperty.version.get();

  • com.google.appengine.application.id è l'ID dell'applicazione. Puoi ottenere l'ID richiamando quanto segue: String ID = SystemProperty.applicationId.get();

  • com.google.appengine.application.version è la versione principale e secondaria del servizio dell'applicazione attualmente in esecuzione, nel formato "X.Y". Il numero di versione principale ("X") è specificato nel file appengine-web.xml del servizio. Il numero di versione secondaria ("Y") viene impostato automaticamente quando ogni versione dell'app viene caricata su App Engine. Puoi ottenere l'ID richiamando quanto segue: String ID = SystemProperty.applicationVersion.get();

    Sul server web di sviluppo, la versione principale restituita è sempre la versione predefinita del servizio e la versione secondaria è sempre "1".

App Engine imposta anche le seguenti proprietà di sistema quando inizializza la JVM su un server delle app:

  • file.separator
  • path.separator
  • line.separator
  • java.version
  • java.vendor
  • java.vendor.url
  • java.class.version
  • java.specification.version
  • java.specification.vendor
  • java.specification.name
  • java.vm.vendor
  • java.vm.name
  • java.vm.specification.version
  • java.vm.specification.vendor
  • java.vm.specification.name
  • user.dir

ID istanza

Puoi recuperare l'ID dell'istanza che gestisce una richiesta utilizzando questo codice:

com.google.apphosting.api.ApiProxy.getCurrentEnvironment().getAttributes().get("com.google.appengine.instance.id")

Nell'ambiente di produzione, 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 specifica. Se l'istanza non è in grado di gestire la richiesta, restituisce immediatamente un errore 503.

ID richiesta

Al momento della richiesta, puoi salvare l'ID richiesta, che è univoco per la richiesta. L'ID richiesta può essere utilizzato in un secondo momento per correlare una richiesta ai log per quella richiesta.

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

com.google.apphosting.api.ApiProxy.getCurrentEnvironment().getAttributes().get("com.google.appengine.runtime.request_log_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 questa intestazione per qualsiasi contenuto statico pubblicato dalla tua app, aggiungi l'intestazione ai gestori di file e directory statici della tua app.

La maggior parte dei framework per app e dei server web supporta l'impostazione di questo header per le risposte generate dal tuo codice. Per informazioni sull'intestazione Strict-Transport-Security in Spring Boot, consulta HTTP Strict Transport Security (HSTS).

Gestione del lavoro asincrono in background

Il lavoro in background è qualsiasi lavoro che la tua app esegue per una richiesta dopo aver fornito la risposta HTTP. Evita di eseguire operazioni in background nella tua app e controlla il codice per assicurarti che tutte le operazioni asincrone terminino prima di fornire la risposta.

Per i job a esecuzione prolungata, 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.