Quiconque a eu à construire un système distribué s’est nécessairement intéressé à la question de la communication entre les composants de ce système. Assez classiquement, un broker de messages peut être mis en œuvre pour introduire certaines fonctionnalités/propriétés aux communications telles que du découplage temporel ou de la fiabilité. C’est particulièrement vrai dans le cadre d’échanges de données inter applicatifs, où ledit système distribué est composé d’une collection de systèmes qui s’échangent des messages.
Vient alors l’épineuse question du choix du broker : idéalement, ce choix devrait se faire sur la base des propriétés attendues pour les communications. Les différences de vue et de compréhension d’une problématique peuvent déjà engendrer un grand nombre de débats sur ce choix. En complément, les organisations humaines étant ce qu’elles sont, il arrive que des considérations peu factuelles entrent en ligne de compte :
NOTE 1 : un choix inadapté ne signifie pas que “ça ne va pas marcher”, mais plutôt qu’on va introduire des biais et/ou nécessiter des mécanismes de compensation plus ou moins inefficaces et disgracieux d’une part et coûteux à implémenter d’autre part.
NOTE 2 : on peut très bien arriver à la conclusion que plusieurs types de broker sont nécessaires sur l’ensemble du périmètre du système distribué. Il est même possible que pour une communication (un flux de messages) donné, plusieurs types de broker soient également nécessaires.
Les 2 biais décrits à l’instant sont – dans mon expérience – particulièrement applicables lorsqu’il s’agit de choisir entre un broker à base de logs d’une part et un broker à base de file d’attente ou à base de souscription (au sens de la classification Gartner, cf le rappel ci-dessous) d’autre part : ce sera le sujet principal de cet article.
Commençons par clarifier rapidement le vocabulaire relatif aux types de broker :
Complétons ces rappels par un petit mot sur les différentes catégories de messages :
Ce distinguo n’est de prime abord pas très exploitable énoncé ainsi : ces raccourcis sont des moyens mnémotechniques qui enveloppent plusieurs considérations, décrites plus bas. Au final, on peut dire que c’est un peu comme l’île de Tortuga où on ne sait aller que si on y a déjà été : ces raccourcis ne sont efficaces que si l’on sait ce qui se cache derrière!
Nous allons progressivement décortiquer cela au fil des chapitres suivants.
Une première question intéressante à se poser est : le système émetteur a-t-il une intention particulière lorsqu’il émet un message en particulier? S’attend-il à ce que quelque chose se produise à l’autre bout de la ligne? Ou encore : a-t-il besoin que quelque chose se produise pour continuer à faire son travail?
Si non, ce système peut être considéré comme un fournisseur de données, qui informe le reste du monde de quelque chose dont lui seul a connaissance à un instant T. Il peut s’agir :
Dans le cas contraire – autrement dit, l’émetteur a une attente par rapport à l’avenir – alors écrire dans un journal n’est probablement pas le plus efficace. Le “producteur” va plutôt émettre une commande et va alors en réalité se comporter comme un client qui consomme un service. Cette commande peut nécessiter :
Je me suis ici focalisé sur les attentes du producteur, mais le même genre de questions peut (et doit) se poser du côté des consommateurs. On va voir au paragraphe suivant que les attentes peuvent être divergentes.
Au paragraphe précédent, j’ai introduit discrètement la problématique des relations entre les systèmes au travers de la notion d’attente ou intention (sous-entendu : l’intention du producteur par rapport au(x) consommateur(s)).
Mais cette problématique va largement plus loin que simplement de savoir ce qu’attend le producteur : le consommateur peut lui aussi avoir certaines attentes, qui ne collent pas nécessairement à la perfection avec celles du producteur. Rien d’étonnant à cela : tout ceci a déjà été largement documenté et discuté dans le cadre du Domain Driven Design (ou DDD – au passage incroyablement riche, instructif et efficace à de nombreux niveaux). Je vais faire ici un raccourci grossier mais efficace : dans le cadre de nos discussions, un broker de messages va servir à matérialiser (ou plutôt motoriser) une frontière entre 2 contextes (plus concrètement, et en première approximation, entre 2 systèmes).
Sauf qu’en réalité, c’est un peu comme à la frontière entre deux pays : il y a deux côtés à une frontière, et les formalités pour passer d’un pays (contexte) à l’autre dépendent des accords (relations) entre ces pays.
Prenons un exemple : un système de gestion d’entrepôt met à disposition un flux quasi temps réel de mises à jour de stock. Il n’attend rien de particulier d’un quelconque consommateur de ces mises à jour de stock : il informe quiconque est intéressé que tel article est sorti de tel entrepôt dans telle quantité (idem pour les entrées en stock). Mais qu’un autre système écoute ou non ces mises à jour ne change rien du tout à la réalité de son monde : quoi qu’il arrive le mouvement de stock s’est produit, que cela intéresse quelqu’un ou non.
De l’autre côté de la frontière, des systèmes/partenaires (par exemple des points de vente ou des sites marchands) ont besoin des mises à jour mais uniquement pour un sous-ensemble bien précis du stock, peut-être avec une fréquence plus faible pour alimenter leur propre cache interne (par exemple une fois par nuit). En complément, on peut également imaginer qu’un autre consommateur soit intéressé par l’intégralité des mises à jours pour effectuer des calculs prédictifs en vue d’adapter l’activité d’une chaîne de production.
Dans ce cas de figure, le producteur ne peut clairement accommoder les exigences divergentes des différents consommateurs. On peut même pousser jusqu’à affirmer que ces exigences ne sont pas son problème.
Comment cela se traduit-il du point de vue terre à terre de la motorisation des frontières? Selon toute vraisemblance, se reposer exclusivement sur un broker à base de logs – ce qui pourrait être une solution adaptée du point de vue du système de gestion d’entrepôt, cf chapitre suivant – est une posture trop limitative ne permettant pas de répondre de manière efficace aux exigences de tous les consommateurs. Dès lors, on se retrouve face au choix de faire porter aux consommateurs certaines fonctionnalités (comme filtrer le flux pour ne prendre que ce qui les intéresse) ou associer 2 types de brokers pour apporter un service plus complet à un plus grand nombre de consommateurs.
Pour pimenter ces réflexions, reste la dimension de la responsabilité : qui fait quoi là dedans? On parle bien ici de gens : quelle équipe a la responsabilité d’implémenter telle ou telle fonctionnalité. En effet chaque développement est une construction socio-technique, c’est-à-dire qu’au-delà du pur “code”, il y a des périmètres de responsabilité en jeu. Ce sujet est loin d’être trivial et il est crucial pour la bonne marche de l’organisation : c’est notamment – sans s’y limiter – le distinguo entre intégration tactique et intégration stratégique, qui mérite un article à part entière.
Pour aller plus loin, un contexte au sens DDD peut avoir besoin d’un broker pour motoriser ses propres besoins/mécanismes internes. Dans l’exemple précédent, le système de gestion d’entrepôt peut être composé de multiples micro-services qui produisent (et consomment) chacun des événements. Ce contexte peut nécessiter de publier ces événements dans un broker à base de journal.
La tentation est alors forte de faire d’une pierre deux coups et donc d’exposer simplement ce broker à des consommateurs extérieurs. Pourquoi pas, mais attention à ne pas confondre les besoins internes avec les besoins des consommateurs externes : il est possible (et même probable) que ce faisant on déporte côté consommateur la charge de certaines fonctionnalités qui pourraient être offertes côté producteur et ainsi être mutualisées.
Par ex : si dans notre exemple des consommateurs n’ont besoin que de sous-ensembles des événements publiés, alors chacun devra lire l’intégralité du log et faire son propre tri. C’est assez inefficace pour un consommateur et ça devient du gaspillage de ressources pour plusieurs consommateurs. Dans ce cas, il peut être plus intéressant d’utiliser les fonctionnalités d’un broker à base de files d’attentes ou de souscriptions pour motoriser les frontières du contexte.
NOTE : il est techniquement possible de faire du “routage” sur la base de la partition dans un broker à base de logs. Attention: c’est une forme de couplage! Cette solution peut être entendable / acceptable au sein d’un même contexte, beaucoup moins pour un contexte tiers.
Jusqu’ici, on a parlé d’événements mais laissé de côté la nuance entre événements discrets et flux d’événements. Prenons un exemple tiré de la physique pour illustrer la nuance : position vs vitesse.
Chaque relevé de position a une valeur en soit et permet de répondre à certains cas d’usage (ex : contrôle de présence dans une zone), mais peut-être que la séquence des positions va apporter une information nécessaire pour répondre à un autre cas d’usage. On peut obtenir la vitesse en extrapolant à partir de la séquence des positions : cela permet effectivement de contrôler la vitesse à partir du relevé des positions.
Autrement dit, le flux des positions a une signification propre, que l’on peut exploiter en plus de chacune des positions distinctes. Une autre manière de le voir est qu’on monte ici d’un niveau d’abstraction par rapport à la donnée de base (comme la dérivée, en mathématiques).
Dans ce genre de scenario, un broker à base de logs est fortement conseillé.
NOTE : Bien que cela sorte largement du cadre de cette discussion, attention néanmoins à garder en tête les limitations de l’analyse de flux en temps réel (si besoin de temps réel) : les seules dimensions d’analyse (indexes) efficaces sont le temps et la clé de partition. Attention également à la profondeur d’analyse qui devrait être restreinte.
De manière connexe, un autre point à prendre en considération est l’importance de la séquence. Pour y voir plus clair, on peut par exemple se poser la question de la gestion d’un cas non nominal dans un flux de messages : que faire quand on “rate” (quelle qu’en soit la raison) un message/événement? Ou qu’on en change l’ordre?
Et en premier lieu : quel impact concret pour le consommateur? Quelle signification fonctionnelle?
Reprenons l’exemple de la gestion d’entrepôt : une erreur d’intégration sur un des événements de stock (ou une interversion d’événements) peut donner lieu à des erreurs de calcul de stock, qui peuvent entrainer des opérations de réassort sur tel ou tel produit. A la clé, une perte d’efficacité de toute la chaîne de traitement et du gaspillage de ressources de l’entreprise.
Dans un tel cas, le choix d’un broker uniquement orienté souscription peut s’avéré risqué et un broker orienté log semble largement plus indiqué. Cependant, la notion d’ordre étant importante, il faudra attacher un soin particulier au partitionnement.
De plus, quel traitement apporter au cas non-nominal? Autrement dit : que faire du message qu’on n’a pas réussi à traiter? Quatre options sont possibles :
Ces problématiques de gestion des cas non nominaux amènent un argument entendu fréquemment, et qui a souvent pour but de faire pencher la balance en direction d’un broker à base de logs, est qu’on peut rejouer des messages depuis un certain point dans le temps. Cet argument est évidemment alléchant, mais il faut faire attention à ne pas confondre un gadget et un requirement.
La question à se poser est donc : pour quel cas d’usage avéré, en réalité?
Ce survol des principales questions à se poser illustre l’importance de l’analyse en intégration dans l’architecture des systèmes distribués. Notons également que j’ai à peine égratigné la surface de sujets fondamentaux tels que l’impact organisationnel des sujets d’intégration.
En guise de conclusion, je propose ci-dessous un raccourci à la hache :
Néanmoins ce n’est qu’un raccourci très personnel et limité, qui n’a de réel intérêt que pour celui ou celle qui a déjà suivi le chemin complet (cf Tortuga 🙂 ), ou nécessiterait probablement d’ajouter des outils complémentaires (comme Kafka Streams ou Stream Analytics) au mix technologique .