La messagerie dans une architecture orientée services

(Joan Zapata) (8 décembre 2020)

Larchitecture orientée services (SOA) a fait ses preuves ses atouts dans pratiquement toutes les industries pour produire des systèmes évolutifs et évolutifs. La SOA a de nombreuses significations, mais son idée fondamentale de décomposer une application complexe en services plus petits, réutilisables et faiblement couplés a parcouru un long chemin depuis la fin des années 1990. Nous avons adopté le concept dès le premier jour et après trois ans, nous avons maintenant une vingtaine de services découplés pilotés par domaine structurant nos applications backend. Cela nous a permis dutiliser les langues que nous aimions – comme Kotlin , Elixir – et les outils nous voulions – comme Event Sourcing – pour la tâche à accomplir.

Mais avoir plusieurs services comporte ses propres défis, le premier étant comment faire communiquer les services entre eux ? Il existe de nombreuses stratégies, et cet article est un retour sur lexpérience de celles que nous utilisons chez Memo Bank.

Commençons par deux services et ce cas dutilisation simple à titre dexemple:

REST via HTTPS

Comme de nombreuses applications backend, nous avons commencé par des appels REST via HTTPS.

Les dépendances cycliques entre les services doivent être évitées à tout prix, donc contrats ne peut pas dire directement aux clients dactiver le nouveau client dès la signature du contrat. Au lieu de cela, les clients doivent envoyer régulièrement des demandes aux contrats pour savoir quand la tâche est terminée.

HTTPS est un bon début, et certainement quelque chose à garder dans la boîte à outils: il est simple à configurer, largement pris en charge et également utilisé pour exposer les API aux applications frontales. Mais il y a un gros problème avec ce schéma, il est synchrone (mais pas nécessairement bloquant). Cela signifie que pendant que les contrats fonctionnent, les clients attendent avec une connexion active. Lorsquil est utilisé à grande échelle, cela signifie que le service le plus lent peut ralentir lensemble du système. Cela signifie également que lorsque le service contrats nest pas disponible, le service clients ne lest pas non plus, ce qui est appelé échec en cascade.

Comment avons-nous abordé ce problème? Nous avons utilisé deux modèles différents: la messagerie discrète et la messagerie continue.

La messagerie discrète

Prenons le premier cas dutilisation: les clients veulent des contrats pour envoyer un contrat.

Pour cela, nous pouvons résoudre le problème en utilisant un mécanisme de messagerie discret: queues . Vous pouvez voir une file dattente comme une boîte aux lettres tierce à haute disponibilité. Même lorsque le service principal nest pas disponible, la boîte aux lettres accepte les messages. Plusieurs instances du même service peuvent consommer la même file dattente, auquel cas la file dattente agit comme un équilibreur de charge et garantit que chaque message est traité au moins une fois. Une fois quun message a été traité, la boîte aux lettres peut loublier.

Nous utilisons des files dattente pour envoyer des commandes (par exemple, «SendContract») dun service à un autre. Il présente plusieurs avantages:

  • Le service dappel ne dépend pas de la disponibilité de le service récepteur, et peut reprendre son travail dès que la commande est dans la file dattente;
  • Le récepteur peut gérer les commandes à son rythme , et nous pouvons facilement faire évoluer le service récepteur vers le haut ou vers le bas en fonction de la charge de ses files dattente;
  • En prime, failure peut facilement être isolé et géré manuellement (nous espérons couvrir le sujet des files dattente de lettres mortes dans un autre article).

Messagerie continue

Voyons maintenant le deuxième cas dutilisation, lorsque le contrat est signé, les contrats doivent le faire savoir aux clients est arrivé pour quil puisse activer le client.

Il est tentant dutiliser une autre file dattente ici, mais comme nous lavons déjà dit, nous ne voulons pas de dépendance cyclique, donc contrats ne sait pas nimporte quoi sur les clients . Ainsi, il ne peut pas envoyer une commande «ActivateCustomer», et il ne peut pas savoir quand ni où envoyer cette commande.

Nous avons résolu ce problème en utilisant un mécanisme de messagerie continue: flux .Vous pouvez voir un flux comme une séquence ordonnée dévénements qui peuvent être consommés en temps réel mais qui sont également rendus disponibles au fil du temps. Contrairement aux files dattente qui sont des commandes éphémères non liées, les flux racontent une histoire persistante .

Chaque service de Memo Bank diffuse un flux de événements décrivant le cycle de vie de ses ressources. La création et la maintenance de ces événements font partie intégrante de tout développement , que cet événement soit immédiatement nécessaire ou non. Cela fait partie de la routine, tout comme les tests et les métriques automatisés.

Ces événements deviennent ainsi un journal horodaté fiable de immuable faits . Et comme ils font partie de lAPI du service émetteur, ils peuvent être consommés par nimporte quel autre service, à la fois en temps réel (un par un) et dans le futur. Ces événements ont une valeur immense pour traçabilité et analyse des données .

Pour résumer:

  • Les files dattente sont utilisées pour envoyer des commandes à un service spécifique;
  • Les flux sont utilisés pour exposer des faits provenant de un service spécifique.

Tout sur les dépendances

Le diagramme ci-dessus pourrait vous amener à vous demander si en regardant les flèches, il nintroduit pas un dépendance cyclique entre clients et contrats?

Non. Une dépendance nest pas définie par la direction des données, mais par la connaissance que les services ont les uns des autres. Ici, clients connaît les contrats , il leur dit quoi faire et il écoute son histoire. Mais contrats ne sait rien des clients , il na pas besoin de savoir qui envoie les commandes, ni qui écoute son histoire.

Les deux la file dattente et le flux font partie de lAPI contrats , et les clients dépendent de cette API.

Avoir un nommer convention pour les commandes et les faits est très importante pour transmettre cette idée. Nous utilisons toujours un formulaire de base pour les commandes, comme «SendContract», et un formulaire de participe passé pour les faits, comme «ContractSent».

Notez quil correspond bien à larchitecture de notre système bancaire principal basé sur CQRS / ES . Dans cette terminologie, les commandes sont les mêmes, et les événements sont des faits.

Comment choisir la direction de la dépendance

Compte tenu des principes expliqués précédemment, cette solution serait tout aussi valable:

Mais si les deux solutions sont valables, comment choisir lune par rapport à lautre? Eh bien, cest à vous de décider.

Tout se résume à la direction de la dépendance que vous voulez à définir.

A: les clients dépendent des contrats . B: contrats dépend des clients .

Voici quelques questions que nous nous posons habituellement:

  • Un service peut-il facilement être indépendant des autres?
    Ici par exemple, à condition de donner le contenu à des contrats , Les contrats peuvent être totalement indépendants du service qui lutilise. Il est plus difficile d’imaginer que les clients sont indifférents au fait que cela nécessite un contrat. Cest en faveur de A.
  • Et si un service était un tiers?
    Pour Par exemple, il naurait pas de sens pour Memo Bank dexternaliser les clients , mais cela pourrait être le cas pour les contrats . Cest donc également en faveur de A.
  • Un service orchestre-t-il dautres services?
    Lorchestration implicite est mauvaise, vous pouvez en savoir plus dans cet exposé de Bernd Ruecker. Créer un client est un workflow complexe qui implique de nombreux services (envoi de-mails, notifications, création dun compte bancaire, etc.), donc clients est probablement un orchestrateur ici.Faire en sorte que lorchestrateur dépende dautres services – et non linverse – rend le code beaucoup plus facile à comprendre, car le flux de travail complet peut être trouvé en un seul endroit. Cest également en faveur de A.
  • Crée-t-il un cycle dans larchitecture globale?
    Même sil ny a pas de lien entre les deux services, ils dépendent tous deux dautres services. Supposons que les clients dépendent des utilisateurs et que les utilisateurs dépendent déjà de contrats . Si nous choisissons la solution B, cela créerait un cycle avec les trois services. C’est également en faveur de A.

Conclusion

La messagerie est l’une des premières questions auxquelles nous devons répondre lors de la création d’une architecture orientée services. Utiliser HTTPS et REST semble être la solution la plus simple au début, mais elle a ses limites. Nous avons complété notre arsenal avec des files dattente et flux , et nous avons défini principalement deux règles.

Premièrement, chaque service doit diffuser événements lorsque des faits se produisent dans ce service, même si nous navons pas encore besoin de ces événements. Ces événements doivent raconter une histoire de ce qui se passe dans le service, comme « ContractSent », « ContractSigned ». Cest parfait pour la traçabilité – qui est nécessaire en tant que banque -, mais aussi pour consolider lAPI de chaque service et pour rendre le système plus facile à utiliser pour toutes les équipes.

Deuxièmement, tout tourne autour de la dépendances . Les dépendances façonnent le système et les dépendances cycliques sont lennemi numéro un. Une fois les dépendances correctement définies, les outils de messagerie sont juste là pour permettre aux données de circuler dans nimporte quelle direction.

Publié à lorigine sur https://memo.bank/en/magazine .

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *