Modèle de nivellement de charge basé sur une file d’attente

Azure Functions
Azure Service Bus

Utilisez une file d’attente qui agit comme une mémoire tampon entre une tâche et un service qu’elle appelle, afin d’atténuer les surcharges intermittentes qui entraînent l’échec du service ou l’expiration de la tâche. Cela permet de réduire l’impact des pics de demande sur la disponibilité et la réactivité de la tâche et du service.

Contexte et problème

De nombreuses solutions cloud impliquent l’exécution de tâches qui appellent des services. Dans cet environnement, si un service est soumis à des surcharges intermittentes, cela peut provoquer des problèmes de performances et de fiabilité.

Un service peut faire partie de la même solution que les tâches qui l’utilisent, ou il peut s’agir d’un service tiers offrant un accès aux ressources fréquemment utilisées, telles qu’un cache ou un service de stockage. Si le même service est utilisé par plusieurs tâches qui s’exécutent simultanément, il peut être difficile de prévoir le volume de requêtes destinées au service un moment donné.

Un service peut connaître des pics de demande qui entraînent sa surcharge et son incapacité à répondre aux requêtes en temps voulu. L’envoi à un service d’un grand nombre de requêtes simultanées peut également entraîner l’échec du service si celui-ci n’est pas en mesure de gérer le conflit causé par ces requêtes.

Solution

Refactorisez la solution et introduisez une file d’attente entre la tâche et le service. La tâche et le service s’exécutent de façon asynchrone. La tâche publie un message contenant les données requises par le service dans une file d’attente. La file d’attente agit comme une mémoire tampon : elle stocke le message jusqu'à ce qu’il soit récupéré par le service. Le service récupère les messages dans la file d’attente et les traite. Les requêtes issues d’un certain nombre de tâches, qui peuvent être générées à une fréquence très variable, peuvent être transmises au service de la même file d’attente de messages. Cette illustration montre l’utilisation d’une file d’attente pour niveler la charge sur un service.

Illustration 1 : utilisation d’une file d’attente pour niveler la charge sur un service

La file d’attente dissocie les tâches du service, et le service peut gérer les messages à son propre rythme, quel que soit le volume de requêtes des tâches simultanées. En outre, il n’y a pas de retard pour une tâche si le service n’est pas disponible au moment de la publication du message dans la file d’attente.

Ce modèle permet de bénéficier des avantages suivants :

  • Il permet d’optimiser la disponibilité, car les retards de services n’ont pas d’impact immédiat et direct sur l’application, qui peut continuer à envoyer des messages à la file d’attente même lorsque le service n’est pas disponible ou ne traite pas actuellement de messages.

  • Il permet d’optimiser l’évolutivité, car à la fois le nombre de files d’attente et le nombre de services peuvent être modifiés pour répondre à la demande.

  • Il permet de contrôler les coûts, car le nombre d’instances de service déployées doit uniquement être suffisant pour répondre à une charge moyenne plutôt qu’à la charge maximale.

    Certains services implémentent une limitation lorsque la demande atteint un seuil au-delà duquel il existe un risque d’échec du système. La limitation peut restreindre les fonctionnalités disponibles. Vous pouvez implémenter le nivellement de la charge avec ces services pour vous assurer que ce seuil n’est pas atteint.

Problèmes et considérations

Prenez en compte les points suivants lorsque vous choisissez comment implémenter ce modèle :

  • Il est nécessaire d’implémenter la logique d’application qui contrôle la fréquence à laquelle les services traitent les messages pour éviter de surcharger la ressource cible. Évitez de transférer des pics de demande à l’étape suivante du système. Testez le système avec la charge pour vous assurer qu’il fournit le niveau requise, puis ajustez le nombre de files d’attente et le nombre d’instances de service qui traitent les messages pour obtenir le résultat souhaité.
  • Les files d’attente de messages constituent un mécanisme de communication unidirectionnelle. Si une tâche attend une réponse d’un service, il peut être nécessaire implémenter un mécanisme que le service peut utiliser pour envoyer une réponse. Pour en savoir plus, voir Primer de messagerie asynchrone.
  • Soyez prudent si vous appliquez la mise à l’échelle automatique aux services qui écoutent les requêtes sur la file d’attente. Cela peut entraîner une augmentation des conflits pour les ressources partagées par ces services et diminuer l’efficacité de la file d’attente pour niveler la charge.
  • En fonction de la charge du service, vous pouvez vous retrouver dans une situation où vous êtes toujours à la traîne, où le système met toujours en file d’attente plus de demandes que vous ne traitez. La variabilité du trafic entrant vers votre application doit être prise en compte
  • Le modèle peut perdre des informations en fonction de la persistance de la file d’attente. Si votre file d’attente se bloque ou supprime des informations (en raison de limites système), il est possible que vous n’ayez pas de livraison garantie. Le comportement de votre file d’attente et les limites système doivent être pris en compte en fonction des besoins de votre solution.

Quand utiliser ce modèle

Ce modèle est utile pour toute application qui utilise des services soumis à une surcharge.

Ce modèle n’est pas utile si l’application attend une réponse du service avec une latence minimale.

Conception de la charge de travail

Un architecte doit évaluer comment le modèle de nivellement de charge basé sur une file d’attente peut être utilisé dans la conception de leur charge de travail pour répondre aux objectifs et principes couverts par les piliers d’Azure Well-Architected Framework. Par exemple :

Pilier Comment ce modèle soutient les objectifs des piliers.
Les décisions relatives à la fiabilité contribuent à rendre votre charge de travail résiliente aux dysfonctionnements et à s’assurer qu’elle retrouve un état de fonctionnement optimal après une défaillance. L’approche décrite dans ce modèle peut fournir une résilience contre les pics soudains de demande en découplant l’arrivée des tâches de leur traitement. Elle peut également isoler les dysfonctionnements dans le traitement de la queue pour qu’ils n’affectent pas la réception.

- RE:06 Mise à l’échelle
- RE :07 Travaux en arrière-plan
L’optimisation des coûts est axée sur le maintien et l’amélioration du retour sur investissement de votre charge de travail. Étant donné que le traitement de la charge est découplé de la réception des demandes ou tâches, vous pouvez utiliser cette approche pour réduire le besoin de surdimensionner les ressources pour gérer la charge de pointe.

- CO:12 Coûts de mise à l’échelle
L’efficacité des performances permet à votre charge de travail de répondre efficacement aux demandes grâce à des optimisations de la mise à l’échelle, des données, du code. Cette approche permet une conception intentionnelle sur la performance du débit parce que la réception des demandes n’a pas besoin de correspondre au taux auquel elles sont traitées.

- PE :05 Mise à l’échelle et partitionnement

Comme pour toute autre décision de conception, il convient de prendre en compte les compromis par rapport aux objectifs des autres piliers qui pourraient être introduits avec ce modèle.

Exemple

Une application web écrit des données dans un magasin de données externes. Si un grand nombre d’instances de l’application web s’exécutent simultanément, le magasin de données risque de ne pas pouvoir répondre aux requêtes assez rapidement, ce qui va entraîner l’expiration du délai d’exécution des requêtes, leur limitation ou leur échec. Le diagramme suivant montre un magasin de données saturé par un grand nombre de requêtes simultanées émanant des instances d’une application.

Figure 2 - Service saturé par un grand nombre de requêtes simultanées émanant des instances d’une application web

Pour résoudre ce problème, vous pouvez utiliser une file d’attente afin de niveler la charge entre les instances d’application et le magasin de données. Une application Azure Functions lit les messages de la file d’attente et exécute les requêtes de lecture/écriture dans le magasin de données. La logique d’application de l’application de fonction peut contrôler la fréquence à laquelle les requêtes sont passées au magasin de données pour éviter la surcharge de ce dernier. (Sinon, l’application de fonction réintroduit simplement le même problème sur le serveur back-end.)

Figure 3 - Utilisation d’une file d’attente et d’une application de fonction pour niveler la charge

Étapes suivantes

Les recommandations suivantes peuvent aussi s’avérer utiles pendant l’implémentation de ce modèle :

  • Primer de messagerie asynchrone. Les files d’attente sont par nature asynchrones. Il peut être nécessaire de reconcevoir la logique d’application dans une tâche si elle est adaptée de sorte à ne plus communiquer directement avec un service mais à utiliser une file d’attente. De même, il peut être nécessaire de refactoriser un service pour accepter les requêtes provenant d’une file d’attente. Sinon, il est parfois possible d’implémenter un service proxy, comme décrit dans l’exemple.

  • Choisir entre les différents services de messagerie Azure. Informations sur le choix d’un mécanisme de messagerie et de file d’attente dans les applications Azure.

  • Communication basée sur des messages asynchrones.

  • Style d’architecture Web-File d’attente-Worker. Le serveur web et le Worker sont sans état. L’état de session peut être stocké dans un cache distribué. N’importe quel travail à exécution longue est traité de façon asynchrone par le Worker. Le Worker peut être déclenché par des messages dans la file d’attente ou s’exécuter selon une planification pour le traitement par lots.

Les modèles suivants peuvent également être pertinents lors de l’implémentation de ce modèle :

  • Modèle des consommateurs concurrents. Il est parfois possible d’exécuter plusieurs instances d’un service, chacune agissant comme un consommateur de messages à partir de la file d’attente de nivellement de charge. Vous pouvez utiliser cette approche pour ajuster la fréquence à laquelle les messages sont reçus et transmis à un service.

  • Modèle de limitation. Un moyen simple d’implémenter la limitation avec un service consiste à utiliser le nivellement de charge basé sur la file d’attente et à acheminer toutes les requêtes vers un service via une file d’attente de messages. Le service peut traiter des requêtes à un rythme qui garantit que les ressources requises par le service ne sont pas épuisées, de façon à réduire le nombre de conflits éventuels.