Event Hubs est un formidable gestionnaire d’évènements, performant et robuste. Associé aux Azure Functions, il fait partie des pierres angulaires de la stratégie Serverless Azure.

Il existe beaucoup de littérature sur comment créer son Event Hubs, comment le sécuriser, comment envoyer des messages dans la file et comment configurer une Azure Function pour ingérer ses messages et effectuer votre traitement personnalisé.
Mais très peu de ces articles évoquent vraiment les problèmes de performances. C’est pourquoi je vous propose aujourd’hui d’aller beaucoup plus loin.

S’il y a une chose essentielle à connaître, c’est qu’il n’existe pas d’implémentation unique absolue. Il n’y a pas de paramétrages magiques applicables à tous. Vous aurez à appréhender, ajuster, tester, peaufiner, retester, puis surveiller constamment.

Don’t Panic : cet article sera votre guide. Il vous donnera les clés pour trouver le plus tôt possible les goulots d’étranglement, identifier les pièges à éviter et surtout savoir quels paramètres tester pour se rapprocher des performances maximales. Mais il vous apportera surtout des outils pour monitorer la performance, qui seront utiles en phase de développement, mais surtout réutilisables en production.

 

Présentation d’Event Hubs

Parler de « file d’attente sécurisée d’évènements » pour présenter Event Hubs peut sembler réducteur, mais je n’en vois pas de définition plus simple mais néanmoins complète.

On peut également évoquer le fait qu’Event Hubs s’intègre à l’écosystème Apache Kafka, ou essayer de le définir par ses innombrables possibilités d’utilisations : IoT, App mobiles, gestionnaire de génération de documents, gestion de stock, file d’attente pour des ressources ayant des capacités limitées, etc.

Schéma de présentation d'Event Hubs

Vision simplifiée d’un Event Hubs

Comme le nom « Event Hubs » l’indique, il peut s’agir d’évènements à traiter. Que ces événements proviennent de données de stations météo, d’une application mobile, d’un bracelet connecté ou d’une application de gestion, les principes décrits dans l’article seront les mêmes.

Nous utiliserons le terme « message » plutôt qu’ « évènement », car c’est le terme le plus souvent utilisé dans les documentations. C’est le message envoyé à l’Event Hubs qui retranscrit l’évènement.

Pour résumer :

  • Des clients émetteurs envoient des messages dans la file d’attente de l’Event Hubs.
  • L’Event Hubs se charge :
    • de sécuriser les accès (clients et consommateurs) ;
    • de recevoir ces messages ;
    • de stocker les messages jusqu’à leur traitement (en succès ou en erreur).
  • Des consommateurs viennent piocher dans une liste de messages à traiter et traitent ces messages.

Version simplifiée d'un Event Hubs

Il existe donc 3 leviers d’optimisation :

  • L’émission des messages par les clients ;
  • L’Event Hubs et la file d’attente : réception, stockage et répartition vers les consommateurs ;
  • La consommation et le traitement des messages.

Les deux derniers points seront traités dans un second article, publié dès la semaine prochaine !

 

Emission des messages par les clients

 

Importance du nombre de connexions

Le premier piège dans lequel tombent de nombreuses personnes est l’implémentation de l’envoi des messages. Le délai d’ouverture et de fermeture d’une connexion vers l’Event Hubs étant non-négligeable, des demandes de connexion trop nombreuses vont affecter les performances et la facturation de l’Event Hubs (voir la partie sur les TUs ci-dessous).

IMPORTANT : il est primordial d’utiliser le minimum de connexions possible à l’Event Hubs.

Retour d’expérience : pour vous donner une idée de l’impact des clients émetteurs de messages sur l’Event Hubs, lors d’une précédente mission, le nombre de messages traités par l’Event Hub était compris entre 1000 et 5000 par minute. Certes, il s’agissait d’une version « démo » mais elle a été utilisée sur un site de production sans avertissement. Cependant, nous recevions 2 à 3 fois plus de requêtes que nous n’arrivions à en traiter. Les requêtes semblaient s’empiler dans l’Event Hubs. Le premier réflexe d’augmenter le nombre de TUs de l’Event Hubs n’a malheureusement eu aucun effet. Pourquoi une telle situation ? Ouvrir une connexion, envoyer le message et fermer cette connexion pour chacune des requêtes coûte énormément de temps processeur (CPU) à l’Event Hubs. Pour régler ce dysfonctionnement, nous avons utilisé une connexion statique, unique à chaque instance d’Azure Function (au lieu d’une connexion par requête) : le nombre de messages traités est alors passé à 250 000 par minute sans aucune autre modification.

Vous trouverez un exemple d’implémentation dans le projet associé à cet article.

Taille des messages

La taille des messages envoyés à l’Event Hubs est également importante. Il ne sert à rien d’envoyer plus que nécessaire. D’abord, parce que la taille des messages de l’Event Hubs est limitée à :

  • 256 kb en tiers Basic ;
  • 1Mo en tiers Standard.

Ensuite, parce que l’impact sur le coût de l’Event Hubs est important (voir la partie Throughput Unit ci-après). Enfin, parce qu’une taille de message plus importante, c’est du temps réseau supplémentaire pour envoyer ledit message.

Pour réduire la taille d’un message, on peut faire en sorte que le client enregistre un document (photo, données brutes, json, …) dans un espace de stockage (blob storage, cosmos db, etc.), puis crée un message avec simplement le chemin du fichier à traiter. Les messages seront alors tout petits et la facture également.

 

Masquer l’Event Hubs

Au-delà des problèmes réseau, masquer la complexité de l’envoi de messages à l’Event Hubs par l’intermédiaire d’une Azure Function (ou d’une autre forme d’API) peut être une très bonne idée. On reste dans l’esprit Serverless tout en ayant une souplesse supplémentaire.

Pour éviter que l’application cliente n’ait à réaliser des traitements complexes, vous pouvez utiliser une Azure Function tampon. Cette dernière se chargera de toute la collecte et la manutention des données avant d’appeler l’Event Hubs.
Le réseau de l’Azure Function étant sans doute meilleur que celui d’un utilisateur en mobilité, le gain est non-négligeable.

Cette solution permettra également de ne pas donner aux clients l’accès à trop de ressources.

Si, par exemple, ce sont des clients IoT dont la configuration est difficile à changer (ex : stations météo d’un lieu éloigné) ou si l’implémentation du client n’est pas réalisée par votre service, il peut être long voire impossible de changer leurs codes ou leurs identifiants de connexion. Avoir un élément intermédiaire sera un plus pour permettre des évolutions telles qu’un changement de structure des messages, le renouvellement de la sécurité, etc.

Démo projet

Vous trouverez à cette adresse le repo git associé à l’article :

https://github.com/newSebastienTHOMAS/EventHubDemoArticle

2 projets sont inclus dans le repo :

  • Un projet d’une Azure Function « client » envoyant des messages sur un Event Hubs avec une connexion statique;
  • Un projet ingérant les messages (nous vous invitons à consulter le prochain article de blog sur ce sujet).

 

L’Event Hubs

Nous aborderons ici les différents paramétrages de l’Event Hubs uniquement dans l’optique des performances.

 

Throughput Units (TU)

 

Définition d’un Throughput Unit

Les Throughput Units sont les unités de contrôle du débit mais également les unités de facturation (nous vous invitons d’ailleurs à consulter la documentation Microsoft sur ce point).

Les TUs ne sont pas des instances de machines qui, par leurs performances, limitent le débit : l’Event Hubs est en effet capable d’ingérer des millions de messages par seconde. Les TUs sont des unités d’allocation, assurant un contrôle ou une limite de la consommation de votre Event Hubs.

On pourrait les comparer à ce qui se passe dans un parc d’attraction : plus on paye cher, moins on doit attendre et plus nombreux on peut passer.

Il est possible de provisionner de 1 à 20 TUs par Namespace EventHubs.

Règles de débit par TU:

  • entrant 1000 messages/seconde
  • entrant 1 Mo/seconde
  • sortant 4096 messages/seconde
  • sortant 2 Mo/seconde

A l’échéance de la première de ces 4 règles, une exception ServerBusyException est levée pour tout message supplémentaire. Ces messages sont en erreur et non-traités par l’Event Hubs.

L’autoscale peut empêcher toute erreur (cf. section suivante).

A NOTER : les appels d’APIs ou d’opérations de management sont désormais comptabilisés dans le comptage des messages. Ce qui explique l’avertissement vu précédemment sur les connexions et leur impact sur les performances et la facturation.

 

TU et mise à l’échelle (auto-inflate)

En plus du nombre de TUs, il est possible d’activer une règle de mise à l’échelle (auto-inflate) et définir un nombre maximum de Tus (et non plus une limite simple).

 

Event Hubs - Mise à l'échelle : TU (Throughput Units) et auto-inflate

Lorsque l’auto-inflate est activé, seuls les TUs actifs sont facturés (et non le nombre de TUs maximum).

Si, lors d’un burst, le nombre de messages augmente et dépasse le seuil des TUs actifs initialement défini, aucune exception ne sera levée et aucun message ne sera perdu. Le contrôleur augmentera automatiquement le nombre de TUs actifs de l’Event Hubs pour satisfaire le nouveau débit maximal, comme si cela avait été fait manuellement.

Attention aux auto-inflates : l’auto-inflate est unidirectionnel. Il ne se fait que vers un niveau de TU supérieur et ne redescend jamais. Les factures de fins de mois peuvent très vite être salées après des tests de charge sur l’environnement de production. Pour à nouveau baisser les TUs, il faudra effectuer manuellement la modification. Il est néanmoins possible de définir des alertes pour vous prévenir ou d’exécuter des scripts à intervalles réguliers qui baisseront à nouveau les TUs si la charge est faible.

 

TU Basic, Standard ou Dédié ?

 

Event Hubs : choix du niveau (princing tier)

Il existe trois niveaux (tiers) de TUs :

  • Basic,
  • Standard
  • Dédié (tarification sur-demande).

Le nombre de consumer groups, le délai de conservation des messages et la possibilité de capturer les messages, diffèrent entre les niveaux Basic et Standard. Ce choix n’a pas cependant pas d’impact sur les performances.

Le nombre de connexions simultanées peut également orienter votre choix selon votre besoin, mais encore une fois, le niveau Basic est tout aussi rapide que le Standard.

 

TU et Performances

Le premier réflexe serait de se dire que, pour améliorer les performances, il faut pousser ce curseur à fond : pourtant, rien n’est plus plus faux.

Au contraire, c’est ce dernier facteur que l’on cherche à changer pour gagner en performance. Une règle de 3 avec les messages/seconde attendus et leur taille permet d’évaluer les coûts possibles.

Il est recommandé de définir le nombre de TUs à 1 et d’activer l’auto-inflate.
Si le besoin s’en fait sentir, l’auto-inflate fera son office sans erreurs et au coût minimum.

 

Event Hubs : Instance et Partitions

 

Event Hubs Consummer groups

Instance d’Event Hubs (ou Entité ou Consumer Group)

Reprenons notre allégorie du parc d’attraction : l’instance (ou l’entité / consumer group), c’est l’attraction. Pour un Event Hubs Namespace, on peut définir jusqu’à 10 instances différentes.
Chaque instance permet de séparer des versions ou des fonctionnalités : avoir une instance pour la facturation et une autre pour la réservation, une instance pour la version 2.2 et une autre pour la version 2.3, etc.

Le choix du nombre d’instances n’a pas d’impact sur la vélocité du traitement.
Notons néanmoins que les instances d’Event Hubs se partagent le nombre de TUs définis au niveau du Namespace. Les TUs seront donc une limite de coût mais pas de performance.

La performance sera plus certainement limitée par la rapidité des consommateurs à traiter les messages. Il est préférable de constituer les instances selon ce qui vous semble le plus logique pour vos flux et vos implémentations.

Le choix des partitions est beaucoup plus cornélien.

⚠️Point de vigilance : le nommage des éléments est  incohérent.

Le nommage des différents éléments de l’event Hub a fortement évolué avec le temps.

J’ai eu beaucoup de mal à comprendre où se situaient les différents objets / éléments associés à un Event Hubs, et plus particulièrement l’instance d’Event Hubs. En effet, selon l’emplacement dans le portail ou les différentes documentations, l’instance porte quatre noms différents : Instance, Entité, Consumer group ou Event Hubs tout court.

=> Dans la capture ci-dessous, vous observerez que sur la page du portail Event Hubs (1) il est affiché, sur la même page, “Entities” (2) puis à nouveau “Event Hubs” (3) puis “+ Event Hub” (4).
Alors qu’il s’agit logiquement, pour les quatre, d’instances ou de Consumer Groups, selon la terminologie de la documentation.

Event Hubs Entity

Dans la documentation et les ARM Templates, il est souvent indiqué Consumer Group (comme vous le verrez sur cette copie d’écran directement tirée de l’aide Microsoft sans modifications).

Event Hubs Consumer groups

Voici l’ARM Template généré pour l’Event Hubs demoeventhubcellenza.

Event Hubs ARM template

Très récemment, les termes “Instance d’Event Hubs et “Event Hubs Namespace” ont été ajoutés à certains endroits du portail : une modification anecdotique qui n’en rend la confusion que plus grande, avec pas moins de quatre noms pour un même objet ! Les modifications de nommage sont si fréquentes que certaines ont même été effectuée entre le début et la fin de la rédaction de cet article…

 

Partitions d’instance d’Event Hubs (ou Consumer Group, ou Partition)

Il est possible de sélectionner un nombre de partitions spécifique pour chaque instance d’Event Hub (Consumer Group) : 2 au minimum et 32 au maximum.

Une demande de plus de 32 partitions peut être faite, mais uniquement auprès du support.

Si votre instance possède 3 partitions, le premier message arrivé sera placé dans la partition n° 1, le deuxième dans la n°2, le troisième dans la n° 3 et le quatrième dans la n°1 à nouveau. Vous l’aurez compris : il s’agit d’une répartition égalitaire sur chaque partition.

Il faut toutefois y faire très attention pour deux raisons :

  • Il n’est pas possible d’ajouter ou de supprimer des partitions sur une instance d’Event Hubs. Le seul moyen de le faire sera de supprimer l’instance et de la recréer, avec le risque de perdre des messages pendant la durée des opérations.
  • Il ne peut y avoir qu’un seul consommateur par partition : c’est une conséquence non négligeable pour les performances. (je vous invite à consulter l’article sur ce sujet qui sera publié la semaine prochaine).

On pourrait être tenté de mettre le maximum de partitions mais le choix peut s’avérer beaucoup plus complexe.

Un premier élément de réponse est que chaque partition a un débit égal à celui d’un TU en entrée et sortie. Choisir 32 partitions peut vous obliger à augmenter fortement les TUs pour obtenir le débit minimum dont vous avez besoin.

La complexité du choix dépend également du temps de traitement de chaque message. Nous verrons dans un prochain article la problématique des consommateurs qui vous demandera sans doute de nombreux tests d’équilibrage.

 

Durée de vie des messages (Message Retention)

Par défaut, les messages ont une durée de vie d’un jour. Elle peut être étendue jusqu’à 7 jours sur un niveau Standard. Vous disposez donc de 1 à 7 jours pour pouvoir traiter le message par un consommateur. Cela peut paraître long, mais il faut garder en tête qu’un problème peut survenir sur un des consommateurs qui reçoit le message à traiter : il peut être bloqué (deadlock, traitement trop long…) et n’est « killé » que plusieurs heures plus tard.

Il faut également savoir qu’un même message peut être envoyé plusieurs fois. Il peut être renvoyé quelques secondes, quelques heures plus tard, mais également plusieurs mois après, et ce même s’il a déjà été traité en succès. Des bugs peuvent en effet survenir et l’Event Hubs renverra alors un message plusieurs fois à votre méthode de traitement.

De la même manière, si vous supprimez un Event Hubs consumer group mais en recréez un nouveau avec le même nom, les messages du premier peuvent être renvoyés sur le second comme de nouveaux messages à traiter. Ce sont les aléas inhérents à l’implémentation de l’Event Hubs.

Dans le cas des envois multiples, ce phénomène est particulièrement gênant, surtout si un message est renvoyé plusieurs heures ou plusieurs semaines après la demande initiale. C’est très rare, mais cela m’est déjà arrivé plusieurs fois sur des instances de régions différentes.

Il vous faudra sans doute implémenter un mécanisme pour ne pas retraiter le même message plusieurs fois.

Note : Lorsque nous évoquons un message « traité en succès », il s’agit d’un message pour lequel le consommateur a terminé le traitement. Même si le traitement dudit message a pu provoquer une exception, il incombe à votre traitement de messages de gérer un mécanisme d’envoi d’un nouveau message sur l’event hub ou de l’enregistrer dans un répertoire à retraiter manuellement.

 

Maintenant que vous avez découvert Event Hubs et savez comment optimiser l’émission des messages par les clients et la gestion de la file d’attente, je vous donne rendez-vous la semaine prochaine : vous saurez tout sur la consommation et le traitement des messages !