Risoluzione dei problemi di latenza elevata nell'app

In molti casi, una latenza elevata nell'applicazione comporterà errori del server 5xx. Pertanto, ha senso seguire una serie simile di passaggi per la risoluzione dei problemi per restringere la causa principale dei picchi di errore e di latenza, dato che le cause di ognuno potrebbero essere le stesse.

Definisci l'ambito del problema

In primo luogo, definisci l'ambito del problema nel modo più ristretto possibile raccogliendo informazioni pertinenti. Di seguito sono riportati alcuni suggerimenti per le informazioni che potrebbero essere pertinenti.

  • Quali ID, servizi e versioni delle applicazioni sono interessati?
  • Quali endpoint specifici dell'app sono interessati?
  • Il problema ha interessato tutti i clienti a livello globale o un sottoinsieme specifico di clienti?
  • Quali sono l'ora di inizio e di fine dell'incidente? Devi specificare il fuso orario.
  • Quali errori specifici riscontri?
  • Qual è il delta di latenza osservato, che di solito viene specificato come aumento in un percentile specifico? Ad esempio, la latenza è aumentata di 2 secondi al 90° percentile.
  • Come hai misurato la latenza? In particolare, è stato misurato sul client o visibile in Cloud Logging e/o nei dati di latenza di Cloud Monitoring forniti dall'infrastruttura di gestione di App Engine?
  • Quali sono le dipendenze della tua applicazione e si sono verificati incidenti?
  • Di recente hai apportato modifiche al codice, alla configurazione o al carico di lavoro che potrebbero aver attivato questo problema?

Un'applicazione può disporre di funzionalità di monitoraggio e logging personalizzati che puoi utilizzare per restringere ulteriormente l'ambito del problema, oltre ai suggerimenti sopra riportati. La definizione dell'ambito del problema ti consentirà di scegliere la causa principale più probabile e stabilirà i passaggi successivi per la risoluzione dei problemi.

Individuare gli errori

Quindi, determina quale componente del percorso di richiesta ha maggiori probabilità di causare latenza o errori. I componenti principali del percorso di richiesta sono:

Client -> Internet -> Google Front End (GFE) -> Infrastruttura di gestione App Engine -> Istanza dell'applicazione

Se le informazioni raccolte nel passaggio 1 non rimandano all'origine dell'errore, in genere è consigliabile iniziare esaminando l'integrità e le prestazioni delle istanze dell'applicazione.

Un modo per determinare se il problema risiede o meno nell'istanza dell'applicazione è esaminare i log delle richieste di App Engine: se noti errori del codice di stato HTTP o una latenza elevata in questi log, in genere significa che il problema risiede nell'istanza che esegue l'applicazione.

Esiste uno scenario in cui errori e latenza elevati nei log delle richieste potrebbero non essere causati dall'istanza dell'applicazione stessa: se il numero di istanze dell'applicazione non è stato fatto lo scale up per corrispondere ai livelli di traffico, le istanze potrebbero essere sovraccaricate, con conseguenti errori e latenza elevati.

Se noti errori o latenza elevati in Cloud Monitoring, in genere puoi concludere che il problema si trova a monte del bilanciatore del carico, che registra le metriche di App Engine. Nella maggior parte dei casi, ciò indica un problema nelle istanze dell'applicazione.

Tuttavia, se noti una latenza elevata o errori nel monitoraggio delle metriche, ma non richieste nei log, potrebbero essere necessarie ulteriori indagini. Potrebbe indicare un errore nel livello di bilanciamento del carico o che le istanze stanno riscontrando un errore così grave che il bilanciatore del carico non può instradare le richieste alle istanze. Per distinguere questi casi, puoi esaminare i log delle richieste immediatamente prima dell'inizio dell'incidente. Se i log delle richieste mostrano un aumento della latenza subito prima dell'errore, significa che le istanze dell'applicazione stavano iniziando a restituire errori prima che il bilanciatore del carico interrompesse il routing delle richieste.

Scenari che potrebbero causare incidenti

Ecco alcuni scenari riscontrati dagli utenti.

Client

Mappare l'IP di un client a una regione geografica

Google risolve il nome host per l'applicazione App Engine nel GFE più vicino al client, in base all'indirizzo IP del client utilizzato nella ricerca DNS. Se il resolver DNS del client non utilizza il protocollo EDNS0, le richieste del client potrebbero non essere instradate al GFE più vicino.

Internet

Scarsa connessione a internet

Esegui questo comando sul client per determinare se il problema è legato alla scarsa connettività a internet.

$ curl -s -o /dev/null -w '%{time_connect}\n' <hostname>

Il valore di time_connect in genere rappresenta la latenza della connessione del client al frontend Google più vicino. Se questa connessione è lenta, puoi risolvere ulteriormente il problema utilizzando traceroute per determinare quale hop sulla rete causa il ritardo.

Puoi eseguire test da client in località geografiche diverse. Le richieste verranno instradate automaticamente al data center Google più vicino, che varia in base alla località del client.

Client con larghezza di banda ridotta

L'applicazione potrebbe rispondere rapidamente, ma la risposta potrebbe essere rallentata da colli di bottiglia della rete che impediscono all'infrastruttura di servizio di App Engine di inviare pacchetti sulla rete con la stessa velocità con cui potrebbero essere inviati.

Google Front End (GFE)

Blocco head of line HTTP/2

I client HTTP/2 che inviano più richieste in parallelo potrebbero riscontrare una latenza elevata a causa del blocco head of line in GFE. La soluzione migliore è che i client eseguano l'upgrade per utilizzare il protocollo QUIC.

Terminazione SSL per i domini personalizzati

GFE termina la connessione SSL. Se utilizzi un dominio personalizzato, anziché un dominio appspot.com, è necessario un hop aggiuntivo per la terminazione SSL. Ciò potrebbe aggiungere latenza per le applicazioni in esecuzione in alcune regioni.

Infrastruttura di gestione di App Engine

Incidente a livello di servizio

Google pubblicherà i dettagli di un problema grave a livello di servizio all'indirizzo https://status.cloud.google.com/. Tieni presente che Google implementa la funzionalità gradualmente, perciò è improbabile che un incidente a livello di servizio interessi tutte le istanze contemporaneamente.

Scalabilità automatica

Fai lo scale up del traffico troppo rapidamente

La scalabilità automatica di App Engine potrebbe non scalare le istanze alla velocità con cui aumenta il traffico, causando un sovraccarico temporaneo. In genere, ciò si verifica quando il traffico non viene generato in modo organico dagli utenti finali, ma viene invece generato da un programma informatico. Il modo migliore per risolvere il problema è limitare il sistema che genera il traffico.

Picchi nel traffico

I picchi di traffico possono causare latenza elevata nei casi in cui un'applicazione con scalabilità automatica debba fare lo scale up più rapidamente del possibile senza influire sulla latenza. In genere, il traffico degli utenti finali non causa picchi di traffico frequenti. In questo caso, dovresti indagare sulla causa dei picchi di traffico. Se un sistema batch viene eseguito a intervalli, potresti essere in grado di rendere più calmo il traffico o utilizzare impostazioni di scalabilità diverse.

Impostazioni del gestore della scalabilità automatica

Il gestore della scalabilità automatica può essere configurato in base alle caratteristiche di scalabilità della tua applicazione. Questi parametri di scalabilità potrebbero non essere ottimali in determinati scenari.

Le applicazioni dell'ambiente flessibile di App Engine scalano in base all'utilizzo della CPU. Tuttavia, l'applicazione potrebbe essere vincolata da I/O durante un incidente che comporta un sovraccarico delle istanze con un numero elevato di richieste, poiché non si verifica la scalabilità basata sulla CPU.

Le impostazioni di scalabilità dell'ambiente standard di App Engine potrebbero causare latenza se impostate in modo troppo aggressivo. Se vedi risposte del server con il codice di stato 500 e il messaggio Request was aborted after waiting too long to attempt to service your request nei tuoi log, significa che la richiesta è scaduta nella coda in attesa in attesa di un'istanza inattiva.

Non utilizzare la scalabilità manuale dell'ambiente standard di App Engine se la tua app gestisce il traffico dell'utente finale. La scalabilità manuale è più adatta per carichi di lavoro come le code di attività. Potresti notare un aumento del tempo di attesa con la scalabilità manuale anche se hai eseguito il provisioning di un numero sufficiente di istanze.

Non utilizzare la scalabilità di base dell'ambiente standard di App Engine per le applicazioni sensibili alla latenza. Questo tipo di scalabilità è progettato per ridurre al minimo i costi a scapito della latenza.

Le impostazioni di scalabilità predefinite dell'ambiente standard di App Engine forniscono una latenza ottimale per la maggior parte delle applicazioni. Se continui a vedere richieste con tempi di attesa elevata, puoi specificare un numero minimo di istanze. Se ottimizzi le impostazioni di scalabilità per ridurre i costi riducendo al minimo le istanze inattive, corri il rischio di riscontrare picchi di latenza se il carico aumenta improvvisamente.

Ti consigliamo di eseguire un benchmark di prestazioni con le impostazioni di scalabilità predefinite e di eseguire un nuovo benchmark dopo ogni modifica a queste impostazioni.

Deployment

La latenza elevata poco dopo un deployment indica che non hai fatto lo scale up sufficiente prima di eseguire la migrazione del traffico. Le istanze più recenti potrebbero non aver riscaldato le cache locali e quindi potrebbero essere eseguite più lentamente rispetto alle istanze meno recenti.

Per evitare picchi di latenza, non eseguire il deployment di un'app App Engine utilizzando lo stesso nome di versione di una versione esistente dell'app. Se riutilizzi un nome di versione esistente, non potrai eseguire lentamente la migrazione del traffico alla nuova versione. Le richieste potrebbero essere più lente perché ogni istanza viene riavviata entro un breve periodo di tempo. Dovrai anche eseguire nuovamente il deployment se vuoi tornare alla versione precedente.

Istanza dell'applicazione

Codice dell'applicazione

Il debug dei problemi nel codice dell'applicazione può essere molto difficile, in particolare se sono intermittenti o non vengono riprodotti facilmente. Per diagnosticare i problemi, ti consigliamo di utilizzare in strumentazione per la tua applicazione l'utilizzo di logging, monitoring e tracciamento. Puoi provare a utilizzare Cloud Profiler per diagnosticare i problemi. Consulta questo esempio di diagnosi della latenza delle richieste di caricamento utilizzando Cloud Trace per caricare informazioni aggiuntive sulla tempistica per ogni richiesta.

Puoi anche provare a riprodurre il problema in un ambiente di sviluppo locale, permettendoti di eseguire strumenti di debug specifici per i linguaggi che potrebbero non essere possibili in App Engine.

Se è in esecuzione nell'ambiente flessibile di App Engine, puoi accedere a un'istanza tramite SSH ed eseguire un thread dump per verificare lo stato attuale dell'applicazione. Puoi provare a riprodurre il problema in un test di carico o eseguendo l'app localmente. Puoi aumentare le dimensioni dell'istanza per vedere se il problema si risolve. Ad esempio, una RAM maggiore potrebbe risolvere i problemi delle applicazioni che stanno subendo ritardi dovuti alla garbage collection.

Per comprendere meglio in che modo l'app non funziona e quali colli di bottiglia si verificano, puoi caricare l'applicazione fino all'errore. Imposta un numero massimo di istanze e poi aumenta gradualmente il carico fino a quando l'applicazione non funziona.

Se il problema di latenza è correlato al deployment di una nuova versione del codice dell'applicazione, puoi eseguire il rollback per determinare se la nuova versione ha causato l'incidente. Se il deployment viene eseguito continuamente, potresti avere deployment sufficientemente frequenti da risultare difficile determinare se l'incidente sia stato causato dal deployment in base al momento in cui si è verificato.

L'applicazione può archiviare le impostazioni di configurazione all'interno di Datastore o altrove. Sarà utile creare una sequenza temporale delle modifiche alla configurazione per determinare se una di queste è in linea con l'inizio di una latenza elevata.

Modifica del carico di lavoro

Una modifica del carico di lavoro potrebbe causare una latenza elevata. Alcune metriche di monitoraggio che potrebbero indicare che il carico di lavoro è cambiato includono qps, nonché l'utilizzo o la latenza delle API. Puoi anche verificare la presenza di modifiche nelle dimensioni delle richieste e delle risposte.

Errori del controllo di integrità

Il bilanciatore del carico dell'ambiente flessibile di App Engine interromperà il routing delle richieste alle istanze che non superano i controlli di integrità. Ciò potrebbe aumentare il carico su altre istanze, con conseguente potenziale errore a cascata. I log Nginx dell'ambiente flessibile di App Engine mostrano le istanze che non superano i controlli di integrità. Analizza i log e il monitoraggio per determinare il motivo per cui l'istanza è in stato non integro oppure configura i controlli di integrità in modo che siano meno sensibili agli errori temporanei. Tieni presente che ci sarà un breve ritardo prima che il bilanciatore del carico smetta di instradare il traffico a un'istanza non integro. Questo ritardo potrebbe causare un picco di errori se il bilanciatore del carico non può riprovare a inviare le richieste.

L'ambiente standard di App Engine non utilizza i controlli di integrità.

Pressione della memoria

Se il monitoraggio mostra un pattern a denti di sega nell'utilizzo della memoria o un calo dell'utilizzo della memoria correlato ai deployment, i problemi di prestazioni possono essere causati da una perdita di memoria. Una perdita di memoria potrebbe causare una garbage collection frequente, con una latenza maggiore. Il provisioning di istanze più grandi con più memoria potrebbe risolvere il problema se non riesci a rintracciarlo facilmente nel codice.

Perdita di risorse

Se un'istanza della tua applicazione mostra una latenza crescente correlata all'età dell'istanza, potresti avere una perdita di risorse che causa problemi di prestazioni. In questo tipo di problema, noterai anche cali di latenza subito dopo un deployment. Ad esempio, una struttura di dati che diventa più lenta nel tempo a causa di un maggiore utilizzo della CPU potrebbe rallentare qualsiasi carico di lavoro legato alla CPU.

Ottimizzazione del codice

Ecco alcuni modi per ottimizzare il codice su App Engine per ridurre la latenza:

  • Lavoro offline: utilizza Cloud Tasks in modo che le richieste degli utenti non blocchino in attesa del completamento del lavoro, come l'invio di posta.

  • Chiamate API asincrone: assicurati che il codice non sia bloccato in attesa del completamento di una chiamata API. Le librerie come ndb offrono supporto integrato a questo scopo.

  • Chiamate API batch: la versione batch delle chiamate API di solito è più veloce rispetto all'invio di singole chiamate.

  • Denormalizza i modelli dei dati: riduci la latenza delle chiamate effettuate al livello di persistenza dei dati denormalizzando i tuoi modelli dei dati.

Dipendenze

Puoi monitorare le dipendenze dell'applicazione in modo da poter rilevare se i picchi di latenza sono correlati a un errore delle dipendenze.

Un aumento della latenza per una dipendenza può essere causato da una modifica del carico di lavoro e da un aumento del traffico.

Dipendenza senza scalabilità

Se la dipendenza non viene scalata con lo scale up del numero di istanze di App Engine, potrebbe sovraccaricarsi con l'aumento del traffico. Un esempio di dipendenza non scalabile è un database SQL. Un numero maggiore di istanze di applicazione porterà a un numero più elevato di connessioni al database, che potrebbero causare errori a cascata impedendo l'avvio del database.

Un modo per risolvere il problema è il seguente:

  1. Esegui il deployment di una nuova versione predefinita che non si connette al database.
  2. Chiudi la versione predefinita precedente.
  3. Esegui il deployment di una nuova versione non predefinita che si connetta al database.
  4. Migra lentamente il traffico alla nuova versione.

Una potenziale misura preventiva è progettare l'applicazione in modo che elimini le richieste alla dipendenza utilizzando la funzionalità Limitazione adattiva.

Errore livello di memorizzazione nella cache

Un buon modo per velocizzare le richieste è quello di utilizzare più livelli di memorizzazione nella cache:

  • Memorizzazione in una cache perimetrale
  • Memcache
  • Memoria in-istanza

Un improvviso aumento della latenza potrebbe essere causato da un errore in uno di questi livelli di memorizzazione nella cache. Ad esempio, uno svuotamento memcache potrebbe causare l'invio di un numero maggiore di richieste al datastore più lento.