Partager via


Modèles de résilience d’application

Conseil

Ce contenu est un extrait du livre électronique, Cloud Native .NET apps for Azure (Architecture d’applications .NET natives cloud pour Azure), disponible dans la documentation .NET ou au format PDF à télécharger gratuitement pour le lire hors connexion.

Cloud Native .NET apps for Azure eBook cover thumbnail.

La première ligne de défense est la résilience des applications.

Vous pouvez consacrer beaucoup de temps à l’écriture de votre propre infrastructure de résilience ou recourir à des solutions existantes. Polly est une bibliothèque .NET complète de résilience et de gestion des erreurs temporaires qui permet aux développeurs d’exprimer des stratégies de résilience de manière fluide et thread-safe. Polly cible les applications générées avec .NET Framework ou .NET 7. Le tableau suivant décrit les fonctionnalités de résilience, appelées policies, disponibles dans la bibliothèque Polly. Elles peuvent être appliquées individuellement ou regroupées.

Policy Expérience
Réessayer Configure les opérations de nouvelle tentative sur les opérations désignées.
Disjoncteur Bloque les opérations demandées pour une période prédéfinie lorsque les erreurs dépassent un seuil configuré
Délai d'expiration Limite la durée pendant laquelle un appelant peut attendre une réponse.
Cloisonnement Limite les actions à un pool de ressources de taille fixe pour empêcher les appels défaillants de submerger une ressource.
Cache Stocke automatiquement les réponses.
Secours Définit un comportement structuré en cas d’échec.

Notez comment, dans la figure précédente, les stratégies de résilience s’appliquent aux messages de requête, qu’ils proviennent d’un client externe ou d’un service back-end. L’objectif est de compenser la demande d’un service qui peut être momentanément indisponible. Ces interruptions de courte durée se manifestent généralement avec les codes d’état HTTP indiqués dans le tableau suivant.

Code d’état HTTP Cause
404 Introuvable
408 Délai d’expiration de la demande
429 Trop de requêtes (une limitation a probablement été appliquée)
502 Passerelle incorrecte
503 Service indisponible
504 Délai d’expiration de la passerelle

Question : Réessayez-vous un code d’état HTTP 403 - Interdit ? Non. Ici, le système fonctionne correctement, mais informe l’appelant qu’il n’est pas autorisé à effectuer l’opération demandée. Veillez à réessayer uniquement les opérations causées par des défaillances.

Comme recommandé dans le chapitre 1, les développeurs Microsoft qui construisent des applications natives cloud doivent cibler la plateforme .NET. La version 2.1 a introduit la bibliothèque HTTPClientFactory pour créer des instances de client HTTP pour interagir avec des ressources basées sur l’URL. La classe de fabrique qui remplace la classe HTTPClient d’origine prend en charge de nombreuses fonctionnalités améliorées, dont une intégration étroite avec la bibliothèque de résilience Polly. Celle-ci vous permet de définir aisément des stratégies de résilience dans la classe de démarrage de l’application pour gérer les défaillances partielles et les problèmes de connectivité.

Ensuite, nous allons développer les modèles de nouvelle tentative et de disjoncteur.

Modèle Nouvelle tentative

Dans un environnement natif du cloud et distribué, les appels aux services et aux ressources cloud peuvent échouer en raison de défaillances temporaires (de courte durée), qui se corrigent généralement après une courte période de temps. L’implémentation d’une stratégie de nouvelle tentative permet à un service natif du cloud d’atténuer ces scénarios.

Le modèle Nouvelle tentative permet à un service de réessayer une opération de requête ayant échoué un nombre de fois (configurable) avec une durée d’attente exponentiellement croissante. La figure 6-2 montre une nouvelle tentative en action.

Retry pattern in action

Figure 6-2 : Modèle de nouvelle tentative en action

Dans la figure précédente, un modèle de nouvelle tentative a été implémenté pour une opération de requête. Il est configuré de façon à permettre un maximum de quatre nouvelles tentatives avant d’échouer avec un intervalle d’interruption (temps d’attente) commençant à deux secondes, qui double de manière exponentielle pour chaque tentative suivante.

  • Le premier appel échoue et retourne un code d’état HTTP de 500. L’application attend deux secondes et réessaye l’appel.
  • Le deuxième appel échoue également et retourne un code d’état HTTP de 500. L’application double désormais l’intervalle d’interruption à quatre secondes et retente l’appel.
  • Enfin, le troisième appel réussit.
  • Dans ce scénario, l’opération de nouvelle tentative aurait tenté jusqu’à quatre nouvelles tentatives tout en doublant la durée d’interruption avant de faire échouer l’appel.
  • Si la quatrième nouvelle tentative avait échoué, une stratégie de secours aurait été appelée pour gérer correctement le problème.

Il est important d’augmenter la période d’interruption avant de réessayer l’appel pour permettre au service de se corriger automatiquement. Il est recommandé d’implémenter une interruption exponentiellement croissante (doublement de la période à chaque nouvelle tentative) pour obtenir un délai de correction adéquat.

Modèle Disjoncteur

Bien que le modèle de nouvelle tentative puisse aider à récupérer une demande enchevêtrée dans une défaillance partielle, il existe des situations où des défaillances peuvent être provoquées par des événements imprévus dont la résolution nécessite davantage de temps. Ces erreurs peuvent aller d’une perte partielle de connectivité à la défaillance complète d’un service. Dans ces cas de figure, il est inutile qu’une application effectue de nouvelles tentatives dont la réussite sera peu probable.

Pour aggraver les choses, l’exécution d’opérations de nouvelle tentative continues sur un service non réactif peut vous faire basculer vers un scénario de déni de service auto-imposé où vous submergez votre service d’appels continuels qui saturent les ressources telles que la mémoire, les threads et les connexions de base de données, provoquant une défaillance dans des parties non liées du système qui utilisent les mêmes ressources.

Dans ces situations, il serait préférable que l’opération échoue immédiatement et que l’appel du service soit tenté uniquement s’il est susceptible de réussir.

Le modèle Disjoncteur peut empêcher qu’une application ne tente d’exécuter à répétition une opération qui échouera probablement. Après un nombre prédéfinis d’appels ayant échoué, il bloque tout le trafic vers le service. Régulièrement, il permet à un appel d’essai de déterminer si l’erreur a été résolue. La figure 6-3 montre le modèle Disjoncteur en action.

Circuit breaker pattern in action

Figure 6-3. Modèle Disjoncteur en action

Dans la figure précédente, un modèle Disjoncteur a été ajouté au modèle de nouvelle tentative d’origine. Notez comment après 100 demandes ayant échoué, les disjoncteurs s’ouvrent et n’autorisent plus les appels au service. La valeur CheckCircuit, définie à 30 secondes, spécifie la fréquence à laquelle la bibliothèque permet la transmission d’une demande au service. Si cet appel aboutit, le circuit se ferme et le service est à nouveau disponible pour le trafic.

N’oubliez pas que l’intention du modèle Disjoncteur est différente de celle du modèle Nouvelle tentative. Le modèle Nouvelle tentative permet à une application de réessayer une opération dans l’espoir qu’elle aboutisse. Le modèle Disjoncteur empêche une application d’effectuer une opération qui échouera probablement. Une application peut combiner ces deux modèles en utilisant le modèle Nouvelle tentative pour appeler une opération par le biais d’un disjoncteur.

Test pour la résilience

Les tests de résilience ne peuvent pas toujours être effectués de la même façon que vous testez les fonctionnalités de l’application (en exécutant des tests unitaires, des tests d’intégration, etc.). Au lieu de cela, vous devez tester le fonctionnement de la charge de travail de bout en bout dans les conditions d’échec qui ne se produisent que par intermittence. Par exemple : injecter des échecs en plantant des processus, en expirant des certificats, en rendant les services dépendants indisponibles, etc. Les frameworks tels que chaos-monkey peuvent être utilisés pour ces tests de chaos.

La résilience des applications est indispensable pour gérer les opérations demandées problématiques. Mais ce n’est que la moitié de l’histoire. Nous abordons ensuite les fonctionnalités de résilience disponibles dans le cloud Azure.