Introduzione
Se la tua app riscontra una latenza superiore al normale, un throughput scarso o timeout con il client Java Datastore, il problema potrebbe derivare dalla configurazione gRPC lato client anziché dal backend Firestore/Datastore. Questa guida ti aiuterà a diagnosticare e risolvere i problemi comuni di limitazione lato client, le impostazioni errate del pool di canali e l'eccessiva rotazione dei canali.
Diagnosi
Quando si utilizzano client Java, l'attivazione della registrazione dettagliata di gRPC e gax-java è fondamentale per monitorare le dinamiche del pool di canali, diagnosticare la limitazione, i problemi di riutilizzo della connessione o identificare l'eccessiva rotazione dei canali.
Abilita la registrazione per i client Java
Per attivare il logging dettagliato, modifica il file logging.properties
nel seguente modo:
## This tracks the lifecycle events of each grpc channel
io.grpc.ChannelLogger.level=FINEST
## Tracks channel pool events(resizing, shrinking) from GAX level
com.google.api.gax.grpc.ChannelPool.level=FINEST
Inoltre, aggiorna il livello di logging dell'output in logging.properties
per acquisire questi log:
# This could be changed to a file or other log output
handlers=java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level=FINEST
java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter
Applica la configurazione
Il file logging.properties
può essere applicato utilizzando uno dei due metodi:
1. Tramite una proprietà di sistema JVM
Aggiungi questo argomento all'avvio dell'applicazione Java:
-Djava.util.logging.config.file=/path/to/logging.properties
2. Carica in modo programmatico
Questo metodo è utile per i test di integrazione o le applicazioni in cui la configurazione della registrazione viene gestita all'interno del codice. Assicurati che questo codice venga eseguito all'inizio del ciclo di vita dell'applicazione.
LogManager logManager = LogManager.getLogManager();
try (final InputStream is = Main.class.getResourceAsStream("/logging.properties")) {
logManager.readConfiguration(is);
}
Esempio di logging
Dopo aver attivato la registrazione dettagliata, vedrai un mix di messaggi di com.google.api.gax.grpc.ChannelPool
e io.grpc.ChannelLogger
:
Canali con provisioning insufficiente che hanno attivato l'espansione del pool di canali:
09:15:30.123 [pool-1-thread-1] DEBUG com.google.api.gax.grpc.ChannelPool - Detected throughput peak of 40, expanding channel pool size: 4 -> 6.
09:15:30.124 [grpc-nio-worker-ELG-1-5] DEBUG io.grpc.ChannelLogger - [Channel<5>: (datastore.googleapis.com:443)] Entering IDLE state
09:15:30.124 [grpc-nio-worker-ELG-1-5] DEBUG io.grpc.ChannelLogger - [Channel<6>: (datastore.googleapis.com:443)] Entering IDLE state
09:15:30.125 [grpc-nio-worker-ELG-1-5] TRACE io.grpc.ChannelLogger - [Channel<5>: (datastore.googleapis.com:443)] newCall() called
09:15:30.126 [grpc-nio-worker-ELG-1-5] DEBUG io.grpc.ChannelLogger - [Channel<5>: (datastore.googleapis.com:443)] Entering CONNECTING state 09:15:30.127 [grpc-nio-worker-ELG-1-5] DEBUG io.grpc.ChannelLogger - [Channel<5>: (datastore.googleapis.com:443)] Entering READY state with picker: Picker{result=PickResult{subchannel=Subchannel<7>: (datastore.googleapis.com:443), streamTracerFactory=null, status=Status{code=OK, description=null, cause=null}, drop=false, authority-override=null}}
09:15:31.201 [grpc-nio-worker-ELG-1-6] TRACE io.grpc.ChannelLogger - [Channel<6>: (datastore.googleapis.com:443)] newCall() called
09:15:31.202 [grpc-nio-worker-ELG-1-6] DEBUG io.grpc.ChannelLogger - [Channel<6>: (datastore.googleapis.com:443)] Entering CONNECTING state 09:15:31.203 [grpc-nio-worker-ELG-1-6] DEBUG io.grpc.ChannelLogger - [Channel<6>: (datastore.googleapis.com:443)] Entering READY state with picker: Picker{result=PickResult{subchannel=Subchannel<8>: (datastore.googleapis.com:443), streamTracerFactory=null, status=Status{code=OK, description=null, cause=null}, drop=false, authority-override=null}}
Canali sottoprovvigionati che hanno attivato la riduzione del pool di canali:
09:13:59.609 [grpc-nio-worker-ELG-1-4] DEBUG io.grpc.ChannelLogger - [Channel<21>: (datastore.googleapis.com:443)] Entering READY state with picker: Picker{result=PickResult{subchannel=Subchannel<23>: (datastore.googleapis.com:443), streamTracerFactory=null, status=Status{code=OK, description=null, cause=null}, drop=false, authority-override=null}}
09:14:01.998 [pool-1-thread-1] DEBUG com.google.api.gax.grpc.ChannelPool - Detected throughput drop to 0, shrinking channel pool size: 8 -> 6.
09:14:01.999 [pool-1-thread-1] TRACE io.grpc.ChannelLogger - [Channel<13>: (datastore.googleapis.com:443)] shutdown() called
09:14:01.999 [pool-1-thread-1] DEBUG io.grpc.ChannelLogger - [Channel<13>: (datastore.googleapis.com:443)] Entering SHUTDOWN state
09:14:01.999 [pool-1-thread-1] DEBUG io.grpc.ChannelLogger - [Channel<13>: (datastore.googleapis.com:443)] Terminated
09:14:01.999 [pool-1-thread-1] TRACE io.grpc.ChannelLogger - [Channel<15>: (datastore.googleapis.com:443)] shutdown() called
09:14:01.999 [pool-1-thread-1] DEBUG io.grpc.ChannelLogger - [Channel<15>: (datastore.googleapis.com:443)] Entering SHUTDOWN state
09:14:01.999 [pool-1-thread-1] DEBUG io.grpc.ChannelLogger - [Channel<15>: (datastore.googleapis.com:443)] Terminated
Queste voci di log sono utili per:
- Monitorare le decisioni di ridimensionamento dei canali
- Identificazione del riutilizzo del canale Tasso di abbandono
- Individuare i problemi di connettività del trasporto
Interpretazione dei log per la diagnosi dei sintomi
Quando risolvi problemi di prestazioni come latenza elevata o errori, inizia attivando la registrazione per com.google.api.gax.grpc.ChannelPool
e io.grpc.ChannelLogger
. Quindi, associa i sintomi che osservi agli scenari comuni di questa guida per interpretare i log e trovare una soluzione.
Sintomo 1: latenza elevata all'avvio o durante i picchi di traffico
Potresti notare che le prime richieste dopo l'avvio dell'applicazione sono lente o che la latenza aumenta notevolmente ogni volta che il traffico aumenta improvvisamente.
Possibile causa
Questa latenza si verifica spesso quando il pool di canali non è sufficiente. Ogni canale gRPC può gestire un numero limitato di richieste simultanee (100 limitate dal middleware di Google). Una volta raggiunto questo limite, le nuove RPC verranno messe in coda sul lato client, in attesa di uno slot disponibile. Questa coda è la principale fonte di latenza.
Sebbene il pool di canali sia progettato per adattarsi alle variazioni di carico, la sua logica di ridimensionamento viene eseguita periodicamente (per impostazione predefinita, ogni minuto) e si espande gradualmente (per impostazione predefinita, aggiungendo al massimo 2 canali alla volta). Pertanto, esiste un ritardo intrinseco tra il picco di traffico iniziale e l'espansione del pool. La latenza si verifica durante questo periodo, mentre le nuove richieste sono in attesa che i canali saturi si liberino o che il pool aggiunga nuovi canali nel successivo ciclo di ridimensionamento.
Log da cercare
Cerca i log che indicano che il pool di canali si sta espandendo. Questa è la prova principale che il client sta reagendo a un picco di traffico che ha superato la sua capacità attuale.
Log di espansione del pool di canali:
[pool-1-thread-1] DEBUG com.google.api.gax.grpc.ChannelPool - Detected throughput peak of 40, expanding channel pool size: 4 -> 6.
Interpretazione: il pool ha rilevato un picco di traffico e sta aprendo nuove connessioni per gestire il carico maggiore. L'espansione frequente, soprattutto all'avvio, è un chiaro segnale che la configurazione iniziale non è sufficiente per il tuo workload tipico.
Come risolvere il problema
L'obiettivo è avere a disposizione un numero sufficiente di canali prima dell'arrivo del traffico, in modo che il cliente non debba crearli sotto pressione.
Aumento di initialChannelCount
(latenza elevata all'avvio):
Se osservi un'elevata latenza all'avvio, la soluzione più efficace è aumentare il valore di initialChannelCount
in ChannelPoolSettings
. Per impostazione predefinita, questo valore è impostato su 10, che potrebbe essere troppo basso per le applicazioni che gestiscono un traffico significativo all'avvio.
Per trovare il valore giusto per l'applicazione, devi:
- Attiva il logging di livello
FINEST
percom.google.api.gax.grpc.ChannelPool
. - Esegui l'applicazione in un tipico workload iniziale o picco di traffico.
- Osserva i log di espansione del pool di canali per vedere a quale dimensione si stabilizza il pool.
Ad esempio, se visualizzi log come questi:
[pool-1-thread-1] DEBUG com.google.api.gax.grpc.ChannelPool - Detected throughput peak of 80, expanding channel pool size: 4 -> 6.
e un minuto dopo vedi i log:
[pool-1-thread-1] DEBUG com.google.api.gax.grpc.ChannelPool - Detected throughput peak of 95, expanding channel pool size: 6 -> 8.
Se le dimensioni del pool di canali espanso si stabilizzano a 8 canali e non è possibile trovare altri log di espansione, significa che la tua applicazione aveva bisogno di 8 canali per gestire il carico di lavoro. Un buon punto di partenza è impostare initialChannelCount
su 8. In questo modo, le connessioni vengono stabilite in anticipo, riducendo o eliminando la latenza causata dallo scaling al volo.
L'obiettivo è garantire che il pool abbia una capacità sufficiente per gestire il carico di picco senza mettere in coda le richieste.
Aumento minChannelCount
(picchi di traffico):
Se riscontri una latenza elevata, in particolare all'inizio dei picchi di traffico, significa che il traffico sta aumentando più rapidamente di quanto il pool di canali possa creare nuove connessioni in modo reattivo. Questo è comune per le applicazioni con picchi di traffico prevedibili e improvvisi.
Il pool si stabilizza con un numero ridotto di canali durante il traffico normale e ne aggiunge proattivamente altri man mano che il traffico aumenta per evitare la saturazione. Questo processo di scalabilità richiede tempo, il che fa sì che le richieste iniziali in un burst vengano messe in coda, con conseguente latenza elevata.
Aumentare minChannelCount
imposta una base di riferimento più elevata per i canali aperti in modo permanente. In questo modo, è già disponibile una capacità sufficiente per gestire il burst iniziale, eliminando il ritardo di scalabilità e il picco di latenza associato.
Aumento maxChannelCount
(picchi di traffico):
Se osservi una latenza elevata durante i picchi di traffico e i log mostrano che il pool di canali è costantemente alla sua dimensione massima (maxChannelCount
), è probabile che il limite attuale sia troppo basso per il traffico di picco. Per impostazione predefinita, maxChannelCount
è impostato su 200. Per determinare un valore migliore, utilizza la guida alla configurazione del pool di connessioni per calcolare il numero ottimale di connessioni in base alle query al secondo (QPS) di picco e alla latenza media delle richieste della tua applicazione.
Sintomo 2: timeout intermittenti o errori RPC
A volte l'applicazione funziona bene per la maggior parte del tempo, ma occasionalmente si verificano timeout intermittenti o RPC non riuscite, anche durante i periodi di traffico normale.
Possibile causa: instabilità della rete
Le connessioni tra il client e il servizio Datastore vengono interrotte. Il client gRPC tenterà di riconnettersi automaticamente, ma questo processo può causare errori temporanei e latenza.
Log da cercare
Il log più importante per la diagnosi dei problemi di rete è TRANSIENT_FAILURE
.
- Log degli errori temporanei:
[grpc-nio-worker-ELG-1-7] DEBUG io.grpc.ChannelLogger - [Channel<9>: (datastore.googleapis.com:443)] Entering TRANSIENT_FAILURE state
- Interpretazione: questo log è un importante segnale di avvertimento che indica che il canale ha perso la connessione. Un singolo errore isolato potrebbe essere solo un piccolo problema di rete. Tuttavia, se questi messaggi vengono visualizzati di frequente o se un canale rimane bloccato in questo stato, significa che c'è un problema di fondo significativo.
Come risolvere il problema
Esamina l'ambiente di rete. Verifica la presenza di problemi con firewall, proxy, router o instabilità generale della rete tra l'applicazione e datastore.googleapis.com
.
Sintomo 3: latenza elevata con oltre 20.000 richieste simultanee per client
Questo sintomo specifico si applica alle applicazioni eseguite su larga scala, in genere quando una singola istanza client deve gestire più di 20.000 richieste simultanee. L'applicazione funziona bene in condizioni normali, ma quando il traffico aumenta a più di 20.000 richieste simultanee per client, si verifica un degrado netto e improvviso delle prestazioni. La latenza aumenta in modo significativo e rimane elevata per l'intera durata del periodo di picco del traffico. Ciò si verifica perché il pool di connessioni del client ha raggiunto la dimensione massima e non può essere scalato ulteriormente.
Possibile causa
Il client soffre di saturazione dei canali perché il pool di canali ha raggiunto il valore maxChannelCount
configurato. Per impostazione predefinita, il pool è configurato con un limite di 200 canali. Poiché ogni canale gRPC può gestire fino a 100 richieste simultanee, questo limite viene raggiunto solo quando una singola istanza client elabora circa 20.000 richieste contemporaneamente.
Una volta raggiunto questo limite massimo, non può creare altre connessioni. Tutti i 200 canali vengono sovraccaricati e le nuove richieste vengono messe in coda sul lato client, causando il picco di latenza.
I seguenti log possono aiutarti a confermare che è stato raggiunto il maxChannelCount
.
Log da cercare
L'indicatore chiave è l'osservazione che il pool di canali smette di espandersi esattamente al limite configurato, anche se il carico continua.
- Log: vedrai espandersi i log precedenti del pool. Con le impostazioni predefinite, l'ultimo log di espansione visualizzato prima dei picchi di latenza sarà quello in cui il pool raggiunge i 200 canali:
[pool-1-thread-1] DEBUG com.google.api.gax.grpc.ChannelPool - ... expanding channel pool size: 198 -> 200.
- Indicatore: durante il periodo di latenza elevata, non vedrai ulteriori log "expanding channel pool size". L'assenza di questi log, combinata con la latenza elevata, è un forte indicatore che è stato raggiunto il limite di
maxChannelCount
.
Come risolvere il problema
L'obiettivo è garantire che il pool abbia una capacità sufficiente per gestire il carico di picco senza mettere in coda le richieste.
- Aumenta
maxChannelCount
: la soluzione principale consiste nell'aumentare l'impostazionemaxChannelCount
a un valore in grado di supportare il picco di traffico dell'applicazione. Consulta la guida alla configurazione del pool di connessioni per calcolare il numero ottimale di connessioni in base alle query al secondo (QPS) di picco dell'applicazione e alla latenza media delle richieste.
Appendice
Le sezioni seguenti forniscono informazioni supplementari per facilitare la risoluzione dei problemi.
Informazioni sugli stati dei canali
Nei log possono essere visualizzati i seguenti stati del canale, che forniscono informazioni sul comportamento della connessione:
Stato | Descrizione |
---|---|
IDLE | Il canale viene creato, ma non ha connessioni o RPC attive. È in attesa del traffico. |
CONNESSIONE IN CORSO | Il canale sta tentando attivamente di stabilire un nuovo trasporto di rete (connessione) al server gRPC. |
PRONTO | Il canale ha un trasporto stabilito e integro ed è pronto a inviare RPC. |
TRANSIENT_FAILURE | Il canale ha riscontrato un errore recuperabile (ad es. problema di rete, mancata disponibilità temporanea del server). Il dispositivo tenterà automaticamente di riconnettersi. |
SHUTDOWN | Il canale è stato chiuso, manualmente (ad es. shutdown() ha chiamato) o a causa di un timeout di inattività. Non è possibile avviare nuove RPC. |
Suggerimenti
- Se utilizzi un framework di logging strutturato come SLF4J o Logback, devi configurare livelli di log equivalenti in
logback.xml
o in altri file di configurazione del logger. I livellijava.util.logging
verranno mappati dal facade di logging.