Partager via


Penser la conception des applications pour la réparation spontanée

Concevez votre application pour qu’elle se répare spontanément en cas de défaillance

Dans un système distribué, les pannes doivent être considérées comme inévitables. Le matériel peut être défaillant. Le réseau peut subir des échecs temporaires. Il est rare qu’un service entier, un centre de données ou même une région Azure subisse une interruption, cependant, même ces scénarios doivent être pris en compte dans l’architecture de votre charge de travail. La résilience et la récupération doivent être abordées dès le début de la conception de votre charge de travail.

Il est donc essentiel de concevoir votre application pour qu’elle se répare spontanément en cas de défaillance. Cela nécessite une approche en trois étapes :

  • Détecter les défaillances.
  • Répondre aux défaillances de manière appropriée.
  • Enregistrer et analyser les défaillances afin de bénéficier d’informations exploitables.

La manière dont vous répondez à un type particulier de défaillance dépend des exigences de disponibilité de votre application. Par exemple, si vous avez besoin d'une haute disponibilité, vous pouvez effectuer un déploiement dans plusieurs zones de disponibilité dans une région. Pour éviter les pannes, même dans le cas peu probable où une région Azure entière serait perturbée, vous pouvez basculer automatiquement vers une région secondaire lors d'une panne régionale. Cependant, cela entraînera un coût plus élevé et des performances potentiellement inférieures à un déploiement dans une seule région.

En outre, ne pensez pas uniquement aux événements importants tels que les pannes de courant régionales qui sont généralement rares. Vous devez vous focaliser tout autant, voire davantage, sur la gestion des défaillances locales et de courte durée, telles que les défaillances de connectivité réseau ou les échecs de connexion aux bases de données.

Recommandations

Utilisez des composants découplés qui communiquent de manière asynchrone. Idéalement, les composants sont découplés en termes de temps et d’espace. Découplés dans le temps signifie que les composants n’ont pas besoin d’être présents en même temps pour que la communication soit possible. Découplés dans l’espace signifie que l’expéditeur et le récepteur n’ont pas à fonctionner dans le même processus, mais peuvent se trouver là où c’est plus efficace. Les composants découplés utilisent idéalement des événements pour communiquer entre eux. Cela permet de minimiser les risques de défaillances en cascade.

Relance des opérations ayant échoué. Les échecs temporaires peuvent se produire en raison d’une perte momentanée de la connectivité réseau, d’une connexion à une base de données interrompue ou d’un délai d’attente lorsqu’un service est occupé. Intégrez une logique de relance à votre application pour gérer les défaillances temporaires. Pour de nombreux services Azure, le Kit de développement logiciel client implémente de nouvelles tentatives automatiques. Pour plus d'informations, consultez Gestion des erreurs temporaires et le Modèle Nouvelle tentative.

Protéger les services distants ayant échoué (Disjoncteur) . Il est judicieux de lancer de nouvelles tentatives après une défaillance temporaire, mais si le problème persiste, vous pouvez vous retrouver avec un trop grand nombre d’appels vers le service défectueux. Cela peut entraîner des échecs en cascade à mesure que les requêtes sont sauvegardées. Utilisez le modèle Disjoncteur pour effectuer un Fail-fast (sans passer l'appel distant) lorsque qu'une opération est susceptible d'échouer.

Isoler les ressources critiques (Cloisonnement) . Un sous-système peut parfois être victime de défaillances en cascade. Cela peut se produire si une défaillance empêche que certaines ressources, telles que des threads ou des sockets, ne soient libérées en temps voulu, menant à un épuisement des ressources. Pour éviter cela, utilisez le modèle Bulkhead pour partitionner un système en groupes isolés, de manière à ce qu’une défaillance présente dans une partition ne détériore pas l’ensemble du système.

Effectuer un nivellement de la charge. Les applications peuvent rencontrer des pics soudains dans le trafic qui peuvent surcharger les services sur le serveur principal. Pour éviter cela, utilisez le Modèle de nivellement de charge basé sur une file d'attente afin de mettre en file d'attente les éléments de travail à exécuter de manière asynchrone. La file d’attente agit comme une mémoire tampon qui lisse des pics de charge.

Effectuer un basculement. Si une instance ne peut pas être atteinte, effectuez un basculement vers une autre instance. Pour les éléments sans état, tels qu’un serveur web, placez plusieurs instances derrière un équilibreur de charge ou un gestionnaire de trafic. Pour les éléments avec état, tels qu’une base de données, utilisez des réplicas et le basculement. Selon la banque de données et la manière dont elle est répliquée, l’application peut avoir besoin de mettre en place la cohérence éventuelle.

Compenser les transactions qui ont échoué. En règle générale, évitez les transactions distribuées, car elles requièrent une coordination entre les services et les ressources. Au lieu de cela, concevez une opération à partir de transactions individuelles de plus petite taille. Si l'opération échoue en cours de traitement, utilisez des transactions de compensation pour annuler les étapes qui ont déjà été effectuées.

Créer des points de contrôle pour les transactions de longue durée. Les points de contrôle peuvent fournir la résilience en cas d’échec d’une opération longue. Lors du redémarrage de l’opération (par exemple si elle est récupérée par une autre machine virtuelle), elle peut être reprise à partir du dernier point de contrôle. Envisagez d’implémenter un mécanisme qui enregistre les informations d’état de la tâche à des intervalles réguliers et d’enregistrer cet état dans un espace de stockage durable accessible par l’ensemble des instances du processus exécutant la tâche. Ainsi, si le processus est arrêté, la tâche qu’il exécutait peut être poursuivie à partir du dernier point de contrôle à l’aide d’une instance différente. Il existe des bibliothèques qui fournissent cette fonctionnalité, telles que NServiceBus et MassTransit. Elles conservent en toute transparence l’état où les intervalles sont alignés sur le traitement des messages à partir de files d’attente dans Azure Service Bus.

Dégradez gracieusement et restez réactif en cas de panne. Parfois, vous ne pouvez pas résoudre le problème, mais vous pouvez fournir des fonctionnalités réduites qui s’avèrent toujours utiles. Imaginez une application qui propose un catalogue de livres. Si l’application ne peut pas récupérer l’image miniature de la couverture, elle affichera une image de substitution. Des sous-systèmes entiers peuvent s’avérer non critiques pour l’application. Par exemple, dans le cas d’un site de commerce en ligne, l’affichage des recommandations de produits est probablement moins important que le traitement des commandes.

Limiter les clients. Parfois, seuls quelques utilisateurs créent une charge excessive, ce qui peut réduire la disponibilité de votre application pour les autres utilisateurs. Dans ce cas, limitez le client pendant un certain temps. Consultez le Modèle de limitation.

Bloquer les acteurs malveillants. Le fait de limiter un client ne signifie pas que celui-ci agissait de façon malveillante. Cela signifie simplement que le client a dépassé son quota de service. Mais si un client dépasse constamment son quota ou se comporte de manière incorrecte, vous pouvez le bloquer. Définissez un processus hors-bande pour que l’utilisateur puisse demander à être débloqué.

Utiliser la fonction d’élection du responsable. Lorsque vous avez besoin de coordonner une tâche, utilisez la fonction Élection du responsable pour sélectionner un coordinateur. De cette façon, le coordinateur ne constitue pas un point de défaillance unique. Si le coordinateur échoue, un autre est sélectionné. Au lieu d’implémenter vous-même un algorithme d’élection du responsable, envisagez d’adopter une solution prête à l’emploi telle que Zookeeper.

Procéder à des tests avec injection d’erreurs. Trop souvent, on teste le chemin d’accès en cas de réussite mais pas celui en cas d’échec. Un système peut s’exécuter en mode de production pendant longtemps avant qu’un chemin d’échec ne soit utilisé. Utilisez l’injection d’erreurs pour tester la résilience du système lors de pannes, en déclenchant des échecs réels ou simulés.

Adopter l’ingénierie du chaos. L’ingénierie du chaos étend la notion d’injection d’erreurs en injectant de manière aléatoire des défaillances ou des conditions anormales dans les instances de production.

Utiliser des zones de disponibilité. De nombreuses régions Azure fournissent des zones de disponibilité, qui sont des ensembles isolés de centres de données au sein de la région. Certains services Azure peuvent être déployés par zone, ce qui garantit qu'ils sont placés dans une zone spécifique et peut aider à réduire la latence de communication entre les composants d'une même charge de travail. Alternativement, certains services peuvent être déployés avec la redondance de zone, ce qui signifie qu'Azure réplique automatiquement la ressource dans les zones pour une haute disponibilité. Déterminez quelle approche offre le meilleur ensemble de compromis pour votre solution. Pour savoir comment concevoir de votre solution afin d’utiliser des zones de disponibilité et des régions, consultez Recommandations relatives à l’utilisation des zones de disponibilité et des régions.

Pour mettre en place une approche structurée permettant la réparation spontanée de vos applications, consultez Conception d'applications fiables pour Azure.