mardi , 19 juin 2018
Home » Articles » Les pièges courants de l’intégration des microservices – comment les éviter ?

Les pièges courants de l’intégration des microservices – comment les éviter ?

Comment surmonter les défis de la communication à distance, de l’asynchronicité et des transactions dans l’infrastructure de microservices ?

Les microservices font fureur. Ils ont une proposition de valeur intéressante, qui permet aux logiciels de commercialiser rapidement tout en développant avec de multiples équipes de développement logiciel. Ainsi, les microservices concernent la mise à l’échelle de votre force de développement tout en conservant une grande agilité et un rythme de développement rapide.

En un mot, vous décomposez un système en microservices. La décomposition n’est pas une nouveauté, mais avec les microservices, vous donnez aux équipes développant des services autant d’autonomie que possible.

Par exemple, une équipe dédiée possède le service et peut le déployer ou le redéployer quand il le souhaite. Ils font généralement aussi des devops pour pouvoir contrôler l’ensemble du service. Ils peuvent prendre des décisions technologiques plutôt autonomes et gérer leur propre infrastructure, par exemples les bases de données. Le fait d’être obligé de faire fonctionner le logiciel limite généralement le nombre de choix de technologie filaire, car les gens ont tendance à choisir la technologie ennuyeuse beaucoup plus souvent lorsqu’ils savent qu’ils devront l’utiliser plus tard.

 

Les microservices concernent la décomposition, mais donnent à chaque composante un haut degré d’autonomie et d’isolement.

Un résultat fondamental de l’architecture des microservices est que chaque microservice est une application séparée communiquant à distance avec d’autres microservices. Cela rend les environnements microservices hautement distribués. Les systèmes distribués ont leurs propres défis. Dans cet article, nous allons vous guider à travers les pièges les plus courants de l’intégration des microservices.

1. La communication est complexe

La communication à distance doit inévitablement respecter les 8 erreurs de la programmation distribuée. Il n’est pas possible de cacher la complexité, et de nombreux efforts pour le faire (par exemple Corba ou RMI) ont échoué lamentablement. Une raison importante est que vous devez concevoir pour échouer dans vos services afin de réussir dans un environnement où l’échec est la nouvelle normale. Mais, il existe des modèles et des cadres communs qui peuvent vous aider à vous en sortir. Commençons par un exemple – une situation réelle que vous pouvez rencontrer assez régulièrement :

Je voulais prendre l’avion vers Londres. Quand j’ai reçu l’invitation à l’enregistrement, je suis allé sur le site Web de la compagnie aérienne, j’ai choisi mon siège et j’ai appuyé sur le bouton pour récupérer ma carte d’embarquement. Il m’a donné la réponse suivante :

Supposons un instant que la compagnie aérienne utilise des microservices (ce qui pourrait ne pas être le cas, mais d’autres compagnies aériennes le font).

La première chose que j’ai remarquée : l’erreur est revenue assez rapidement, et d’autres parties du site se sont comportées normalement. Donc, ils ont utilisé le modèle important « fail fast ». Une erreur dans la génération du code à barres n’a pas affecté l’ensemble du site Web. En effet, je pourrais faire tout le reste ; mais je n’ai tout simplement pas pu obtenir la carte d’embarquement. « Fail fast » est bien, car il empêche les erreurs locales d’endommager le système entier. Les modèles bien connus dans ce domaine sont les disjoncteurs, les cloisons et les mailles de service. Ces modèles sont vitaux pour la survie des systèmes distribués.

Échouer rapidement n’est pas suffisant

Mais, échouer rapidement n’est pas suffisant. Il décharge la gestion des échecs aux clients. Dans ce cas, vous devez faire une nouvelle tentative. Dans la situation ci-dessus, il faut attendre le lendemain jusqu’à ce que les problèmes soient résolus pour obtenir la carte d’embarquement ! En effet, vous devriez utiliser vos propres outils pour persister dans la nouvelle tentative (le calendrier par exemple) et vous assurer de ne pas oublier de le faire le lendemain.

Pourquoi la compagnie aérienne ne fait-elle pas la même chose ? Ils connaissent les coordonnées de ses clients et peuvent leur envoyer la carte d’embarquement de manière asynchrone chaque fois qu’elle sera prête à être envoyée. La meilleure réponse aurait été :

Cela serait non seulement beaucoup plus pratique, mais réduirait également la complexité globale, car le nombre de composants qui ont besoin de voir l’échec est réduit :

Vous pouvez adopter le même principe à la communication de service à service. Chaque fois qu’un service peut résoudre les échecs lui-même, il encapsule un comportement important. Cela rend la vie de tous les clients beaucoup plus facile et l’API beaucoup plus propre. La résolution des échecs peut concerner l’état (certains l’appellent longue exécution). On considère la gestion d’état comme une question clé pour la gestion des défaillances dans les microservices.

Bien sûr, le comportement décrit ci-dessus n’est pas toujours ce que vous voulez et la remise de l’échec au client peut être très bien. Mais, cela devrait être une décision consciente qui est faite en fonction des besoins de l’entreprise.

La plupart du temps, une autre raison fait que l’on évite les tentatives de réitération : cela concerne la complexité de la gestion de l’état. Le service doit être ressayé pendant des minutes, des heures ou des jours. Il doit le faire de manière fiable (rappelez-vous : je veux ma carte d’embarquement même s’il y a un redémarrage du système entre les deux), et cela implique la gestion de l’état persistant.

Comment gérer l’état persistant ?

Il y a deux manières typiques de gérer l’état persistant : Vous pouvez le stocker dans une base de données, ou vous pouvez utiliser un moteur de workflow léger ou une machine d’état.

La première méthode – stocker des entités dans une base de données – commence de manière très directe, mais elle entraîne généralement beaucoup de complexité accidentelle. Vous avez non seulement besoin de la table de base de données, mais aussi d’un composant du planificateur pour effectuer la nouvelle tentative. Vous avez probablement besoin d’un composant de surveillance pour voir ou modifier les tâches en attente. Et vous devez vous préoccuper de la gestion des versions si la logique métier globale change pendant que vous souhaitez effectuer la nouvelle tentative. Ainsi de suite.

Cette façon de penser conduit beaucoup de développeurs à ignorer une gestion correcte des défaillances comme décrit ci-dessus, conduisant à une complexité accrue de l’ensemble de l’architecture – et une mauvaise expérience client.

Au lieu de cela, il faut tirer parti des moteurs de workflow légers ou des machines d’état. Ces moteurs sont construits pour conserver l’état persistant et gérer les besoins ultérieurs autour du langage de flux, de la surveillance et des opérations, de la mise à l’échelle pour gérer des volumes élevés, etc.

Il y a quelques moteurs légers sur le marché. Beaucoup d’entre eux utilisent la norme ISO BPMN pour définir les flux et sont open source. Là, on va utiliser le moteur de workflow open source de Camunda pour illustrer le principe de base. Pour le cas d’utilisation simple esquissé ci-dessus, un workflow peut être facilement créé en utilisant un Java DSL :

Bpmn.createExecutableProcess(“generateBoardingPass”)
  .startEvent()
  .serviceTask().camundaClass(Generate3dBarcodeAdapter.class)
    .camundaAsyncBefore().camundaFailedJobRetryTimeCycle(“R30/PT15M”)
  .serviceTask().camundaClass(SendBoardingPass.class)
    .camundaAsyncBefore().camundaFailedJobRetryTimeCycle(“R5/PT1M”)
.endEvent();

À lire aussi

Les objets et autres composants Kubernetes

Les objets Kubernetes et les charges de travail Alors que les conteneurs sont le mécanisme …