Introduction
Si votre application rencontre une latence plus élevée que d'habitude, un débit faible ou des délais d'attente avec le client Java Datastore, le problème peut provenir de la configuration gRPC côté client plutôt que du backend Firestore/Datastore. Ce guide vous aidera à diagnostiquer et à résoudre les problèmes courants de limitation côté client, les paramètres incorrects du pool de canaux et le turnover excessif des canaux.
Diagnostic
Lorsque vous utilisez des clients Java, il est essentiel d'activer la journalisation détaillée de gRPC et gax-java pour surveiller la dynamique du pool de canaux, diagnostiquer la limitation du débit, les problèmes de réutilisation des connexions ou identifier un brassage excessif des canaux.
Activer la journalisation pour les clients Java
Pour activer la journalisation détaillée, modifiez le fichier logging.properties
comme suit :
## 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
De plus, mettez à jour le niveau de journalisation des sorties dans logging.properties
pour capturer ces journaux :
# 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
Appliquer la configuration
Le fichier logging.properties
peut être appliqué de deux manières :
1. Via une propriété système JVM
Ajoutez cet argument lorsque vous démarrez l'application Java :
-Djava.util.logging.config.file=/path/to/logging.properties
2. Charger de manière programmatique
Cette méthode est utile pour les tests d'intégration ou les applications dans lesquelles la configuration de la journalisation est gérée dans le code. Assurez-vous que ce code s'exécute tôt dans le cycle de vie de l'application.
LogManager logManager = LogManager.getLogManager();
try (final InputStream is = Main.class.getResourceAsStream("/logging.properties")) {
logManager.readConfiguration(is);
}
Exemple de journalisation
Après avoir activé la journalisation détaillée, vous verrez un mélange de messages provenant de com.google.api.gax.grpc.ChannelPool
et io.grpc.ChannelLogger
:
Chaînes sous-provisionnées qui ont déclenché l'expansion du pool de chaînes :
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}}
Chaînes surprovisionnées ayant entraîné la réduction du pool de chaînes :
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
Ces entrées de journal sont utiles pour :
- Surveiller les décisions de redimensionnement des chaînes
- Identifier la réutilisation de chaînes par rapport à Churn
- Identifier les problèmes de connectivité de transport
Interpréter les journaux pour diagnostiquer les symptômes
Lorsque vous résolvez des problèmes de performances tels qu'une latence élevée ou des erreurs, commencez par activer la journalisation pour com.google.api.gax.grpc.ChannelPool
et io.grpc.ChannelLogger
. Ensuite, faites correspondre les symptômes que vous observez avec les scénarios courants de ce guide pour interpréter les journaux et trouver une solution.
Problème 1 : Latence élevée au démarrage ou lors des pics de trafic
Vous remarquerez peut-être que les premières requêtes après le démarrage de votre application sont lentes, ou que la latence augmente considérablement chaque fois que le trafic augmente soudainement.
Cause possible
Cette latence se produit souvent lorsque votre pool de canaux est sous-provisionné. Chaque canal gRPC peut gérer un nombre limité de requêtes simultanées (100, limité par le middleware Google). Une fois cette limite atteinte, les nouveaux RPC sont mis en file d'attente côté client, en attendant un emplacement disponible. Cette mise en file d'attente est la principale source de latence.
Bien que le pool de canaux soit conçu pour s'adapter aux changements de charge, sa logique de redimensionnement s'exécute périodiquement (par défaut, toutes les minutes) et se développe progressivement (par défaut, en ajoutant au maximum deux canaux à la fois). Il existe donc un délai inhérent entre le pic de trafic initial et l'expansion du pool. La latence se produira pendant cette période, tandis que les nouvelles requêtes attendront que les canaux saturés soient libres ou que le pool ajoute de nouveaux canaux lors de son prochain cycle de redimensionnement.
Journaux à consulter
Recherchez les journaux indiquant que le pool de canaux est en expansion. Il s'agit de la principale preuve que le client réagit à un pic de trafic qui a dépassé sa capacité actuelle.
Journal d'expansion du pool de canaux :
[pool-1-thread-1] DEBUG com.google.api.gax.grpc.ChannelPool - Detected throughput peak of 40, expanding channel pool size: 4 -> 6.
Interprétation : le pool a détecté un pic de trafic et ouvre de nouvelles connexions pour gérer l'augmentation de la charge. Une expansion fréquente, en particulier au démarrage, indique clairement que votre configuration initiale n'est pas suffisante pour votre charge de travail habituelle.
Comment résoudre le problème
L'objectif est de disposer de suffisamment de chaînes avant l'arrivée du trafic, afin d'éviter que le client n'ait à les créer dans l'urgence.
Augmentation de initialChannelCount
(latence élevée au démarrage) :
Si vous constatez une latence élevée au démarrage, la solution la plus efficace consiste à augmenter la valeur de initialChannelCount
dans ChannelPoolSettings
. Par défaut, cette valeur est définie sur 10, ce qui peut être trop faible pour les applications qui gèrent un trafic important au démarrage.
Pour trouver la valeur appropriée pour l'application, vous devez :
- Activez la journalisation au niveau
FINEST
pourcom.google.api.gax.grpc.ChannelPool
. - Exécutez votre application sous une charge de travail de démarrage typique ou un pic de trafic.
- Consultez les journaux d'expansion du pool de canaux pour voir à quelle taille le pool se stabilise.
Par exemple, si vous voyez des journaux comme celui-ci :
[pool-1-thread-1] DEBUG com.google.api.gax.grpc.ChannelPool - Detected throughput peak of 80, expanding channel pool size: 4 -> 6.
et une minute plus tard, vous voyez les journaux :
[pool-1-thread-1] DEBUG com.google.api.gax.grpc.ChannelPool - Detected throughput peak of 95, expanding channel pool size: 6 -> 8.
Si la taille du pool de canaux étendu est stabilisée à huit canaux et qu'aucun autre journal d'expansion n'est trouvé, cela indique que votre application avait besoin de huit canaux pour gérer sa charge de travail. Un bon point de départ consiste à définir initialChannelCount
sur 8. Cela garantit que les connexions sont établies à l'avance, ce qui réduit ou élimine la latence causée par la mise à l'échelle à la volée.
L'objectif est de s'assurer que le pool dispose d'une capacité suffisante pour gérer la charge maximale sans mettre les requêtes en file d'attente.
Augmentation de minChannelCount
(pics de trafic) :
Si vous constatez une latence élevée au début des pics de trafic, cela signifie que votre trafic augmente plus rapidement que le pool de canaux ne peut créer de connexions de manière réactive. Cela arrive souvent pour les applications avec des pics de trafic soudains et prévisibles.
Le pool se stabilise avec un petit nombre de canaux en cas de trafic normal et en ajoute de manière proactive à mesure que le trafic augmente pour éviter la saturation. Ce processus de scaling prend du temps, ce qui entraîne la mise en file d'attente des requêtes initiales d'un pic, ce qui se traduit par une latence élevée.
Augmenter minChannelCount
définit une référence plus élevée pour les canaux ouverts en permanence. Cela garantit qu'une capacité suffisante est déjà disponible pour gérer le pic initial, ce qui élimine le délai de scaling et le pic de latence associé.
Augmentation de maxChannelCount
(pics de trafic) :
Si vous constatez une latence élevée lors des pics de trafic et que vos journaux indiquent que le pool de canaux est constamment à sa taille maximale (maxChannelCount
), il est probable que la limite actuelle soit trop basse pour votre trafic de pointe. Par défaut, maxChannelCount
est défini sur 200. Pour déterminer une meilleure valeur, utilisez le guide de configuration du pool de connexions afin de calculer le nombre optimal de connexions en fonction du pic de requêtes par seconde (RPS) et de la latence moyenne des requêtes de votre application.
Problème 2 : Délais avant expiration ou échecs RPC intermittents
Il arrive que l'application fonctionne correctement la plupart du temps, mais qu'elle subisse parfois des délais d'inactivité intermittents ou des RPC en échec, même pendant les périodes de trafic normal.
Cause possible : instabilité du réseau
Les connexions entre le client et le service Datastore sont interrompues. Le client gRPC essaiera de se reconnecter automatiquement, mais ce processus peut entraîner des échecs temporaires et une latence.
Journaux à consulter
Le journal le plus important pour diagnostiquer les problèmes de réseau est TRANSIENT_FAILURE
.
- Journal des échecs temporaires :
[grpc-nio-worker-ELG-1-7] DEBUG io.grpc.ChannelLogger - [Channel<9>: (datastore.googleapis.com:443)] Entering TRANSIENT_FAILURE state
- Interprétation : ce journal est un signal d'alarme majeur indiquant que la chaîne a perdu sa connexion. Une seule défaillance isolée peut n'être qu'un petit problème de réseau. Toutefois, si ces messages s'affichent fréquemment ou si une chaîne reste bloquée dans cet état, cela indique un problème sous-jacent important.
Comment résoudre le problème
Examinez votre environnement réseau. Vérifiez s'il existe des problèmes de pare-feu, de proxy, de routeur ou d'instabilité générale du réseau entre l'application et datastore.googleapis.com
.
Problème 3 : Latence élevée avec plus de 20 000 requêtes simultanées par client
Ce symptôme spécifique s'applique aux applications qui s'exécutent à très grande échelle, généralement lorsqu'une seule instance cliente doit gérer plus de 20 000 requêtes simultanées. L'application fonctionne bien dans des conditions normales, mais lorsque le trafic dépasse 20 000 requêtes simultanées par client, vous constatez une dégradation brutale et soudaine des performances. La latence augmente considérablement et reste élevée pendant toute la période de pic de trafic. Cela se produit parce que le pool de connexions du client a atteint sa taille maximale et ne peut pas effectuer un scaling horizontal davantage.
Cause possible
Le client souffre d'une saturation des canaux, car le pool de canaux a atteint sa limite configurée de maxChannelCount
. Par défaut, le pool est configuré avec une limite de 200 chaînes. Étant donné que chaque canal gRPC peut gérer jusqu'à 100 requêtes simultanées, cette limite n'est atteinte que lorsqu'une seule instance de client traite environ 20 000 requêtes simultanément.
Une fois que le pool atteint ce plafond, il ne peut plus créer de connexions. Les 200 canaux sont tous surchargés et les nouvelles requêtes sont mises en file d'attente côté client, ce qui provoque le pic de latence.
Les journaux suivants peuvent vous aider à confirmer que la limite maxChannelCount
a été atteinte.
Journaux à consulter
L'indicateur clé est l'observation que le pool de canaux cesse de s'étendre exactement à sa limite configurée, même si la charge se poursuit.
- Journaux : les journaux précédents du pool se développent. Avec les paramètres par défaut, le dernier journal d'expansion que vous verrez avant les pics de latence sera celui où le pool atteint 200 canaux :
[pool-1-thread-1] DEBUG com.google.api.gax.grpc.ChannelPool - ... expanding channel pool size: 198 -> 200.
- Indicateur : pendant la période de latence élevée, aucun autre journal "Taille du pool de canaux en expansion" ne s'affichera. L'absence de ces journaux, combinée à la latence élevée, indique fortement que la limite
maxChannelCount
a été atteinte.
Comment résoudre le problème
L'objectif est de s'assurer que le pool dispose d'une capacité suffisante pour gérer la charge maximale sans mettre les requêtes en file d'attente.
- Augmenter
maxChannelCount
: la principale solution consiste à augmenter le paramètremaxChannelCount
à une valeur pouvant prendre en charge le pic de trafic de l'application. Consultez le guide de configuration du pool de connexions pour calculer le nombre optimal de connexions en fonction du nombre maximal de requêtes par seconde (RPS) et de la latence moyenne des requêtes de l'application.
Annexe
Les sections suivantes fournissent des informations supplémentaires pour vous aider à résoudre les problèmes.
Comprendre les états des chaînes
Les états de canal suivants peuvent apparaître dans les journaux, ce qui permet de mieux comprendre le comportement de la connexion :
État | Description |
---|---|
IDLE | La chaîne est créée, mais ne comporte aucune connexion ni RPC actifs. Il est en attente du trafic. |
CONNEXION | Le canal tente activement d'établir un nouveau transport réseau (connexion) vers le serveur gRPC. |
À VOS MARQUES | Le canal dispose d'un transport établi et opérationnel, et est prêt à envoyer des RPC. |
TRANSIENT_FAILURE | Le canal a rencontré une défaillance récupérable (par exemple, une brève interruption du réseau ou une indisponibilité temporaire du serveur). Il tentera automatiquement de se reconnecter. |
SHUTDOWN | La chaîne a été fermée manuellement (par exemple, shutdown() appelé) ou en raison d'un délai d'inactivité. Aucun nouveau RPC ne peut être lancé. |
Conseils
- Si vous utilisez un framework de journalisation structurée tel que SLF4J ou Logback, vous devez configurer des niveaux de journaux équivalents dans
logback.xml
ou d'autres fichiers de configuration de l'enregistreur. Les niveauxjava.util.logging
seront mappés par la façade de journalisation.