Pour réussir l’implémentation ou la migration vers une architecture microservices, un certain nombre de problématiques techniques sont à étudier en amont, en parallèle du découpage fonctionnel du SI.
Aujourd’hui nous traiterons de deux problématiques dans notre approche de mise en œuvre d’une architecture microservice :
- Communication clients / services
- Communication entre services
Communication clients / services
Le consommateur de mon service doit-il avoir un accès direct à celui-ci ou faut-il utiliser un composant de méditation intermédiaire comme un API Gateway ?
Un API Gateway est un composant logiciel qui expose nos services à un client. Ainsi le client n’a d’échange qu’avec la passerelle et pas directement avec nos services.
L’API Gateway peux même être découpé pour éventuellement adresser les clients différents.
On parle alors de modélisation « Backends for frontends ».
L’utilisation de la passerelle d’API offre les avantages suivants :
- Il dissocie les clients des services. Les services peuvent être versionnés ou refactorisés sans avoir à mettre à jour tous les clients.
- Les services peuvent utiliser des protocoles de messagerie qui ne sont pas compatibles avec le web, comme AMQP.
- La passerelle d’API peut exécuter d’autres fonctions transverses, telles que l’authentification, la journalisation, la terminaison SSL et l’équilibrage de charge.
- La passerelle peux se charger de la découverte et indexation des services
Attention, la passerelle ne contient pas de fonctionnel, il ne s’agit que d’un « passe plat ».
La communication entre services
Les microservices vont communiquer entre eux. Comment doivent-ils procéder ?
Question primordiale à mon sens, en effet la complexité des microservices n’est pas dans leurs code source, mais dans leurs échanges.
Il y a deux possibilités pour faire communiquer nos services :
- Communication synchrone
- Communication asynchrone
Il n’y a pas de solutions meilleure que l’autre et le bon procédé est celui qui correspond à nos besoins.
Effectuer des appels synchrones
Le service client envoie une requête à un second service, il attend pendant le traitement de la requête et il récupère la réponse du service en fin de traitement.
Les appels synchrones sont les plus simples à mettre en œuvre. Les services communiqueront par appels REST et s’échangeront les données dans le format de leur choix (XML / JSON / binaire).
Mais attention, nos microservices doivent être instanciable automatiquement pour répondre à une hausse de la charge par exemple. Compte tenu de leur « volatilité », comment se trouve-t-il sur le réseau s’ils ont besoin de communiquer ?
Nous avons ici définis un nouveau besoin de composant logiciel :
- Auquel les microservices se déclarent lors de leur initialisation.
- Qui enregistre les adresses de chaque microservices.
- Qui route les appels vers le bon destinataire.
Ce composant se nomme “service de noms”.
Effectuer des appels asynchrones
Ces appels se font en utilisant un bus d’échange de message. Chaque service envoie des messages sur le bus qui seront consommés ensuite par d’autres services. L’émetteur publie sur le bus. Le ou les destinataires qui se sont abonnés à ce type d’évènements sur le bus sont notifié et peuvent alors récupérer des données.
L’émetteur de l’évènement n’a pas de connaissances des clients qui s’abonnent. Ce type d’appels permet de moins coupler les services entre eux. En effet chaque service n’a qu’un seul correspondant : le bus de messages.
Cette façon de communiquer entre applications nous rappelle les fameux ESB (Entreprise Service Bus) adopté massivement par les SI il y a quelques années. Ces composants vendus par des sociétés tierces ont souvent causé bien des problèmes aux DSI. Mais la ou les ESB étaient « intelligent » (transformation, orchestration, routage) un bus de service sert uniquement de canal de transport. Son seul rôle est de stocker l’information en attendant qu’un service tiers la récupère.
La deuxième architecture avec le bus de messages à beaucoup d’avantages mais est plus lourde à mettre en œuvre.
Cette implémentation permet une plus grande flexibilité que les communications synchrones classiques :
- Découplage entre les services : le client envoie sa requête sur un canal sans connaître le service qui va la traiter.
- Message tampon : les messages sont placés dans des files d’attente et seront traités de façon asynchrone par le service même s’il n’est pas disponible au moment de l’envoi du message.
- Communications interprocessus explicites : il n’y a pas de différences entre un appel à un service local ou à distance.
- L’asynchronisme rend plus robuste et plus tolérant le système. Le bus permet de scaler horizontalement tout type de services simplement et efficacement.
Nous venons de traiter deux types d’architectures microservices. La première avec communication par service REST est certainement la plus simple à mettre en œuvre. Elle est efficace et rapide à implémenter lorsqu’il s’agit de migrer depuis une application monolithique. La seconde est plus lourde, mais également plus modulaire et tolérante aux pannes.
Il est par ailleurs tout à fait envisageable de mixer les deux types d’architecture afin d’obtenir une communication hybride. Ceci peut par exemple être envisagé lors d’une migration progressive vers du tout asynchrone.
Dans tous les cas, quel que soit le modèle choisis, il est fondamental de documenter, via des diagrammes de séquences par exemple, les dépendances entre service, car c’est ici que se situe la complexité de votre système d’informations !