Résoudre les problèmes de latence élevée dans votre application

Dans de nombreux cas, une latence élevée dans votre application entraînera à terme des erreurs de serveur 5xx. Par conséquent, il est judicieux de suivre un ensemble d'étapes de dépannage similaires pour isoler la cause première des pics d'erreur et de latence, étant donné que ces causes peuvent être identiques.

Définir la portée du problème

Commencez par rassembler des informations pertinentes pour définir la portée du problème le plus précisément possible. Vous trouverez ci-dessous quelques suggestions d'informations susceptibles d'être pertinentes.

  • Quels sont les ID, services et versions de l'application concernés ?
  • Quels points de terminaison spécifiques sur l'application sont affectés ?
  • Ce problème a-t-il eu un impact sur tous les clients du monde entier ou sur un sous-ensemble de clients spécifique ?
  • Quelles sont les heures de début et de fin de l'incident ? Vous devez spécifier le fuseau horaire.
  • Quelles erreurs spécifiques rencontrez-vous ?
  • Quel est le delta de latence observé, généralement spécifié sous la forme d'une augmentation à un centile spécifique ? Par exemple, la latence a augmenté de deux secondes au 90e centile.
  • Comment avez-vous mesuré la latence ? En particulier, a-t-elle été mesurée au niveau du client ou est-elle visible dans Cloud Logging et/ou dans les données de latence de Cloud Monitoring fournies par l'infrastructure de diffusion App Engine ?
  • Quelles sont les dépendances de votre application et l'une d'entre elles a-t-elle subi des incidents ?
  • Avez-vous récemment apporté des modifications au code, à la configuration ou à la charge de travail qui auraient pu déclencher ce problème ?

Une application peut disposer de sa propre surveillance et journalisation personnalisées, que vous pouvez utiliser pour affiner davantage la portée du problème au-delà des suggestions ci-dessus. Définir l'étendue du problème vous orientera vers la cause probable et déterminera les prochaines étapes de dépannage.

Identifier les échecs

Ensuite, déterminez quel composant du chemin de requête est le plus susceptible de provoquer la latence ou les erreurs. Les principaux composants du chemin de requête sont les suivants :

Client -> Internet -> Google Front End (GFE) -> Infrastructure de diffusion App Engine -> Instance d'application

Si les informations collectées à l'étape 1 ne vous indiquent pas la source de la défaillance, vous devez généralement commencer par examiner l'état et les performances de vos instances d'application.

Pour déterminer si le problème réside dans votre instance d'application, vous pouvez consulter les journaux de requêtes App Engine. Si vous constatez des erreurs de code d'état HTTP ou une latence élevée dans ces journaux, cela signifie généralement que le problème se trouve dans l'instance qui exécute votre application.

Il existe un scénario dans lequel les erreurs et la latence élevées peuvent ne pas être dues à l'instance d'application elle-même : si le nombre d'instances de votre application n'a pas augmenté pour correspondre aux niveaux de trafic, vos instances peuvent être surchargées, ce qui entraîne des erreurs et une latence élevées.

Si vous constatez des erreurs ou une latence élevées dans Cloud Monitoring, vous pouvez généralement constater que le problème se situe en amont de l'équilibreur de charge, qui enregistre les métriques App Engine. Dans la plupart des cas, cela pointe vers un problème dans les instances d'application.

Toutefois, si vous constatez une latence élevée ou des erreurs dans les métriques de surveillance, mais pas dans les journaux de requête, une analyse plus approfondie peut être nécessaire. Cela peut indiquer une défaillance dans la couche d'équilibrage de charge ou une défaillance sur les instances si importante que l'équilibreur de charge ne peut pas acheminer les requêtes vers ces instances. Pour faire la distinction entre ces cas, vous pouvez consulter les journaux de requête juste avant le début de l'incident. Si les journaux de requêtes affichent une latence croissante juste avant l'échec, cela indique que l'échec des instances d'application a commencé avant que l'équilibreur de charge ne cesse d'y acheminer les requêtes.

Scénarios susceptibles de provoquer des incidents

Voici quelques scénarios que les utilisateurs ont rencontrés.

Client

Mapper une adresse IP client à une région géographique

Google résout le nom d'hôte de l'application App Engine sur le GFE le plus proche du client, en fonction de l'adresse IP du client utilisée dans la résolution DNS. Si le résolveur DNS du client n'utilise pas le protocole EDNS0, les requêtes client peuvent ne pas être acheminées vers le GFE le plus proche.

Internet

Connectivité Internet de mauvaise qualité

Exécutez la commande suivante sur votre client pour déterminer si le problème provient d'une connexion Internet de mauvaise qualité.

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

La valeur de time_connect représente généralement la latence de connexion du client au Google Front End le plus proche. Si cette connexion est lente, vous pouvez continuer à résoudre les problèmes à l'aide de traceroute pour déterminer quel saut sur le réseau est à l'origine du retard.

Vous pouvez exécuter des tests à partir de clients situés dans différents emplacements géographiques. Les requêtes sont automatiquement acheminées vers le centre de données Google le plus proche, qui varie en fonction de l'emplacement du client.

Clients disposant d'une faible bande passante

L'application peut répondre rapidement, mais la réponse peut être ralentie par des goulots d'étranglement du réseau qui empêchent l'infrastructure de diffusion App Engine d'envoyer des paquets sur le réseau aussi rapidement que possible.

Google Front End (GFE)

Blocage en tête de ligne HTTP/2

Les clients HTTP/2 qui envoient plusieurs requêtes en parallèle peuvent constater une latence élevée en raison du blocage en tête de ligne au niveau du GFE. La meilleure solution consiste à mettre à niveau les clients pour qu'ils utilisent le protocole QUIC.

Terminaison SSL pour les domaines personnalisés

Le GFE met fin à la connexion SSL. Un saut supplémentaire est requis pour la terminaison SSL si vous utilisez un domaine personnalisé plutôt qu'un domaine appspot.com. Cela peut augmenter la latence des applications exécutées dans certaines régions.

Infrastructure de diffusion App Engine

Incident à l'échelle du service

Les informations sur les incidents graves à l'échelle d'un service sont publiées à l'adresse https://status.cloud.google.com/. Notez que Google effectue des déploiements de manière progressive. Il est donc peu probable qu'un incident à l'échelle du service affecte toutes vos instances en même temps.

Autoscaling

Effectuer un scaling à la hausse du trafic trop rapidement

Il est possible que l'autoscaling App Engine ne puisse pas faire évoluer vos instances aussi rapidement que l'augmentation du trafic, ce qui entraîne une surcharge temporaire. Cela se produit généralement lorsque le trafic n'est pas généré de manière naturelle par les utilisateurs finaux, mais plutôt par un programme informatique. La meilleure façon de résoudre ce problème consiste à limiter le système qui génère le trafic.

Pics de trafic

Des pics de trafic peuvent entraîner une latence élevée dans les cas où une application avec autoscaling doit évoluer plus rapidement que possible sans affecter la latence. Le trafic des utilisateurs finaux n'entraîne généralement pas de pics de trafic fréquents. Si ce message s'affiche, vous devez rechercher l'origine des pics de trafic. Si un système de traitement par lot s'exécute à intervalles réguliers, vous pourrez peut-être lisser le trafic ou utiliser des paramètres de scaling différents.

Paramètres de l'autoscaler

L'autoscaler peut être configuré en fonction des caractéristiques de scaling de votre application. Ces paramètres de scaling peuvent ne pas être optimaux dans certains scénarios.

Les applications de l'environnement flexible App Engine évoluent en fonction de l'utilisation du processeur. Toutefois, l'application peut être liée aux E/S lors d'un incident, ce qui entraîne une surcharge des instances avec un grand nombre de requêtes, car le scaling basé sur le processeur ne se produit pas.

Les paramètres de scaling de l'environnement standard App Engine peuvent provoquer une latence s'ils sont définis de manière trop agressive. Si vous voyez des réponses du serveur avec le code d'état 500 et le message Request was aborted after waiting too long to attempt to service your request dans vos journaux, cela signifie que la requête a expiré dans la file d'attente en attendant une instance inactive.

N'utilisez pas le scaling manuel de l'environnement standard App Engine si votre application diffuse le trafic des utilisateurs finaux. Le scaling manuel est préférable pour les charges de travail telles que les files d'attente de tâches. Vous constaterez peut-être une augmentation du délai d'attente avec le scaling manuel, même lorsque vous avez provisionné suffisamment d'instances.

N'utilisez pas le scaling de base de l'environnement standard App Engine pour les applications sensibles à la latence. Ce type de scaling est conçu pour minimiser les coûts au détriment de la latence.

Les paramètres de scaling par défaut de l'environnement standard App Engine offrent une latence optimale pour la plupart des applications. Si vous voyez toujours des requêtes avec un délai d'attente élevé, vous pouvez spécifier un nombre minimal d'instances. Si vous ajustez les paramètres de scaling de façon à réduire les coûts en minimisant les instances inactives, vous risquez de rencontrer des pics de latence en cas d'augmentation soudaine de la charge.

Nous vous recommandons de comparer les performances avec les paramètres de scaling par défaut, puis d'exécuter un nouveau benchmark après chaque modification apportée à ces paramètres.

Déploiements

Une latence élevée peu de temps après un déploiement indique que le scaling à la hausse que vous avez effectué avant de migrer le trafic n'est pas suffisant. Les instances plus récentes peuvent ne pas avoir vidé les caches locaux et peuvent donc être diffusées plus lentement que les instances plus anciennes.

Pour éviter les pics de latence, ne déployez pas une application App Engine avec le même nom de version qu'une version existante de l'application. Si vous réutilisez un nom de version existant, vous ne pourrez pas migrer progressivement le trafic vers la nouvelle version. Les requêtes peuvent être plus lentes, car chaque instance sera redémarrée dans un court laps de temps. Vous devrez également effectuer un redéploiement si vous souhaitez revenir à la version précédente.

Instance d'application

Code d'application

Les problèmes liés au code d'application peuvent être très difficiles à déboguer, en particulier s'ils sont intermittents ou difficiles à reproduire. Pour faciliter le diagnostic des problèmes, nous vous recommandons d'instrumenter votre application avec la journalisation, la surveillance et le traçage. Vous pouvez essayer de diagnostiquer les problèmes à l'aide de Cloud Profiler. Consultez cet exemple de diagnostic de la latence des requêtes de chargement à l'aide de Cloud Trace pour importer des informations temporelles supplémentaires pour chaque requête.

Vous pouvez également essayer de reproduire le problème dans un environnement de développement local, ce qui peut vous permettre d'exécuter des outils de débogage spécifiques à un langage qui ne peuvent pas être exécutés dans App Engine.

Dans l'environnement flexible App Engine, vous pouvez vous connecter en SSH à une instance et effectuer un vidage de thread pour afficher l'état actuel de votre application. Vous pouvez essayer de reproduire le problème en effectuant un test de charge ou en exécutant l'application localement. Vous pouvez augmenter la taille de l'instance pour voir si cela résout le problème. Par exemple, une RAM plus importante peut résoudre des problèmes pour les applications qui subissent des retards dus à la récupération de mémoire.

Pour mieux comprendre les échecs de votre application et identifier les goulots d'étranglement, vous pouvez tester la charge de votre application jusqu'à ce que celle-ci échoue. Définissez un nombre maximal d'instances, puis augmentez progressivement la charge jusqu'à ce que l'application échoue.

Si le problème de latence est lié au déploiement d'une nouvelle version du code de votre application, vous pouvez effectuer un rollback pour déterminer si la nouvelle version est à l'origine de l'incident. Si vous effectuez des déploiements en continu, il est possible que leur fréquence empêche de déterminer si le déploiement a causé ou non l'incident en fonction de l'heure à laquelle il a eu lieu.

Votre application peut stocker les paramètres de configuration dans le datastore ou ailleurs. Il sera utile de créer une chronologie des modifications de configuration afin de déterminer si l'une de ces lignes correspond à l'apparition d'une latence élevée.

Modification de la charge de travail

Une modification de la charge de travail peut entraîner une latence élevée. Certaines métriques de surveillance pouvant indiquer une modification de la charge de travail incluent les RPS, ainsi que l'utilisation ou la latence de l'API. Vous pouvez également vérifier les modifications apportées à la taille des requêtes et des réponses.

Échecs de la vérification de l'état

L'équilibreur de charge de l'environnement flexible App Engine cesse d'acheminer les requêtes vers des instances qui échouent aux vérifications d'état. Cela peut augmenter la charge sur d'autres instances et entraîner une défaillance en cascade. Les journaux Nginx de l'environnement flexible App Engine affichent les instances qui échouent aux vérifications d'état. Analysez vos journaux et la surveillance pour déterminer pourquoi l'instance a cessé de fonctionner ou configurez les vérifications d'état pour qu'elles soient moins sensibles aux échecs temporaires. Notez qu'un court délai est nécessaire avant que l'équilibreur de charge arrête d'acheminer le trafic vers une instance non opérationnelle. Ce délai peut entraîner un pic d'erreur si l'équilibreur de charge ne peut pas relancer les requêtes.

L'environnement standard App Engine n'utilise pas les vérifications d'état.

Sollicitation de la mémoire

Si la surveillance montre un modèle d'utilisation de la mémoire en dents de scie ou une baisse d'utilisation de la mémoire liée aux déploiements, des problèmes de performances peuvent être dus à une fuite de mémoire. Une fuite de mémoire peut entraîner une récupération fréquente de mémoire, ce qui entraîne une latence plus élevée. Le provisionnement d'instances plus volumineuses avec plus de mémoire peut résoudre le problème si vous ne parvenez pas à le repérer facilement dans le code.

Fuite de ressources

Si une instance de votre application présente une latence croissante liée à son ancienneté, il peut s'agir d'une fuite de ressources provoquant des problèmes de performances. Dans ce type de problème, vous constaterez également une baisse de la latence juste après un déploiement. Par exemple, une structure de données qui devient plus lente au fil du temps en raison d'une utilisation plus élevée du processeur peut ralentir la charge de travail liée au processeur.

Optimisation du code

Voici quelques façons d'optimiser le code sur App Engine pour réduire la latence :

  • Travailler hors connexion : utilisez Cloud Tasks pour que les requêtes des utilisateurs ne soient pas bloquées en attendant l'exécution d'une tâche, comme l'envoi d'e-mails.

  • Appels d'API asynchrones : assurez-vous que votre code n'est pas bloqué en attendant la fin d'un appel d'API. Les bibliothèques telles que ndb offrent une compatibilité intégrée avec cette fonctionnalité.

  • Appels d'API par lot : la version par lot des appels d'API est généralement plus rapide que l'envoi d'appels individuels.

  • Dénormaliser les modèles de données : réduisez la latence des appels passés sur la couche de persistance des données en dénormalisant vos modèles de données.

Dépendances

Vous pouvez surveiller les dépendances de votre application afin de détecter si les pics de latence sont corrélés à l'échec d'une dépendance.

L'augmentation de la latence d'une dépendance peut être due à une modification de la charge de travail, ainsi qu'à une augmentation du trafic.

Dépendance sans scaling

Si votre dépendance n'évolue pas à mesure que le nombre d'instances App Engine augmente, elle peut être surchargée lorsque le trafic augmente. Une base de données SQL est un exemple de dépendance susceptible de ne pas évoluer. Un nombre plus élevé d'instances d'application entraîne une augmentation du nombre de connexions à la base de données, ce qui peut entraîner des défaillances en cascade en empêchant le démarrage de la base de données.

Pour résoudre ce problème, vous pouvez procéder comme suit :

  1. Déployez une nouvelle version par défaut qui ne se connecte pas à la base de données.
  2. Fermez la version par défaut précédente.
  3. Déployez une nouvelle version autre que celle par défaut, qui se connecte à la base de données.
  4. Migrez progressivement le trafic vers la nouvelle version.

Une mesure préventive potentielle consiste à concevoir votre application de façon à supprimer les requêtes adressées à la dépendance à l'aide de la limitation adaptative.

Échec de la couche de mise en cache

Un bon moyen d'accélérer les requêtes consiste à utiliser plusieurs couches de mise en cache :

  • Mise en cache périphérique
  • Memcache
  • Mémoire de l'instance

Une augmentation soudaine de la latence peut être due à une défaillance de l'une de ces couches de mise en cache. Par exemple, un vidage Memcache peut entraîner l'envoi d'un plus grand nombre de requêtes au datastore plus lent.