Êtes-vous sûr d'utiliser des microservices?

Bonjour, Habr!

La pratique montre qu'avec la pertinence continue du paradigme des microservices, les interprétations , les critiques et même la démystification ne manquent pas. Par conséquent, revenant à nos publications traduites, nous avons décidé de parler spécifiquement des microservices , ou plutôt, d'envisager une réponse détaillée à la question posée dans le titre de l'article.

Le terme «microservices» est apparu pour la première fois vers 2011/2012. Depuis lors, sans microservices dans l'environnement informatique, presque nulle part. Cependant, après tant d'années, avons-nous réussi à bien comprendre ce que sont les microservices?

Voyons d'abord quelques définitions:

Les microservices sont une approche du développement logiciel (...), selon laquelle une application est structurée comme un ensemble de services faiblement couplés . Dans une architecture de microservices, les services sont très détaillés et les protocoles utilisés sont légers . L'avantage de décomposer une application en services individuels plus petits est d'améliorer la modularité . Sous cette forme, l'application est plus facile à comprendre, à développer, à tester et l'application elle-même devient plus résistante à l'érosion de l'architecture. Avec cette approche de l'architecture, le développement est parallélisé et les petites équipes autonomes ont la possibilité de développer , déployer et faire évoluer de manière indépendante les services dont elles sont responsables. Toujours avec cette approche, une refactorisation continue peut être appliquée à l'architecture de chaque service. Les architectures de microservices sont également conçues pour une livraison et un déploiement continus .
en.wikipedia.org/wiki/Microservices

Voici un autre passage de Martin Fowler:

Le terme «architecture de microservices» a été fermement ancré au cours des dernières années pour décrire une façon particulière de concevoir de telles applications, chacune étant un ensemble de services déployés indépendamment . Bien qu'il n'y ait pas de définition exacte de ce style architectural, il existe un certain nombre de caractéristiques générales concernant l'organisation de l'application autour de ses capacités commerciales, le déploiement automatisé, la collecte d'informations sur les terminaux et la gestion décentralisée des langues et des données .
martinfowler.com/articles/microservices.html

Vous avez probablement remarqué que la principale similitude entre ces deux formulations est l'indépendance des services et des options. Essayons maintenant de répondre à quelques questions et essayons de savoir si le système que vous implémentez a une base de microservice!

Votre système vous permet-il de redémarrer les services indépendamment?

Ou avez-vous besoin de redémarrer les services dans un certain ordre? Ce comportement peut être dicté par des systèmes de cluster spécifiques où le nœud se connecte au nœud d'origine, par exemple, dans le cluster Akka, s'il n'est pas géré correctement. D'autres cas peuvent être associés à des connexions ou des sockets à longue durée de vie qui ne peuvent être ouvertes que d'un côté. N'oubliez pas que la communication doit rester simple et légère. Encore mieux s'il est complètement asynchrone et basé sur des messages. Toutes les restrictions qui font que les services dépendent les uns des autres à long terme peuvent se transformer en difficultés d'assistance. Des problèmes avec l'un des services peuvent entraîner des problèmes dans tous les autres services qui en dépendent, ce qui peut un jour entraîner une défaillance globale grave.

Pouvez-vous déployer des services indépendamment?

Dans certains cas, la libération et le déploiement de tous les services du système doivent avoir lieu simultanément. Ce processus prend plusieurs jours. Bien sûr, dans ce cas, les possibilités d'introduire un déploiement continu sont limitées, des retards se produisent et la réactivité s'aggrave. Il faut trop de temps pour traiter les bogues urgents, simplement en raison des coûts associés au processus de déploiement. Lorsque différentes équipes sont occupées à prendre en charge différents services, toute dépendance de déploiement peut empêcher d'autres équipes de déployer leurs services, ce qui entraînera la suspension du travail et son efficacité diminuera.

Une modification peut-elle entraîner la nécessité d'effectuer des modifications synchronisées sur d'autres services?

On peut imaginer les nombreuses raisons menant à ce comportement. Tout d'abord, ce sont des changements d'API. Il se trouve qu'il suffit d'ajouter un nouveau champ à la demande d'API pour que la compatibilité entre les services soit rompue. Cependant, il existe des solutions de contournement pour résoudre ces situations:

  • Vous souhaitez peut-être conserver la compatibilité descendante obligatoire pour toutes les modifications apportées à l'API; c'est-à-dire que chaque fois qu'un changement fondamental est effectué, une nouvelle version de l'API est ajoutée. Cela ne signifie pas que vous devrez toujours prendre en charge de nombreuses versions de l'API; vous n'en aurez besoin que jusqu'à la fin de la migration. Vous pouvez simplement parler à d'autres équipes et vous assurer que tout le monde a terminé la migration, ou activer des métriques pour juger si une API spécifique est toujours utilisée.
  • Vous pouvez également envisager cette option: déployer plusieurs versions de votre application à la fois, permettant à d'autres services de basculer vers la dernière version, puis désactiver l'ancienne.
  • Essayez également d'utiliser des formats qui prennent en charge l'évolution des circuits, par exemple Avro , où lors de l'ajout d'un nouveau champ, la compatibilité ne peut pas être violée. Lorsqu'une valeur pour un champ est manquante, la valeur par défaut peut être utilisée.

Une autre raison du déploiement synchronisé est l'utilisation de code partagé par différents services. Selon la façon dont le processus de publication est construit dans la partie partagée du code, toute modification peut nécessiter une mise à jour dans tous les services.

Un changement de base de données entraîne-t-il un changement dans de nombreux services?

Oui, la communication via une base de données est un contre-modèle bien connu. Dans une telle situation, il est nécessaire de mettre à jour le schéma et de modifier simultanément le code de plusieurs microservices à la fois. Il vaut mieux s'assurer qu'un seul service est responsable de la gestion du circuit. Dans ce cas, il devient beaucoup plus facile de maintenir la base de données (c'est-à-dire d'effectuer ses mises à jour) et d'implémenter les versions. Si vous êtes déjà dans une situation d'utilisation de base de données partagée, essayez de limiter le nombre d'opérations d'écriture afin qu'un autre service ne fonctionne que pour la consommation.

Pouvez-vous mettre à jour une dépendance ou une version de Java dans un seul service?

En théorie, cela devrait toujours être possible, non? Mais vraiment pas. Il existe de nombreux exemples de bibliothèques "partagées" ou simplement des projets qui contiennent des définitions de toutes les dépendances utilisées dans le système. L'objectif du paradigme des microservices est de s'assurer que chacun d'eux est une entité indépendante, de plus, différents microservices n'ont même pas à être écrits dans la même pile technologique. Lors du démarrage d'un nouveau projet, vous devriez être en mesure de décider par vous-même quelle technologie lui convient le mieux. Toute liaison entraînera de nouveaux problèmes. À un moment donné, vous devrez peut-être mettre à niveau vers Scala ou JDK, mais bien sûr, vous ne voulez pas le faire tout de suite dans tout le système. Dans un système où le niveau de détail est correctement observé, vous pouvez mettre à jour un service individuel, le déployer, le tester et le surveiller pendant plusieurs jours ou semaines, puis, si tout est correct, vous pouvez effectuer des mises à jour dans le reste du système. Cependant, lorsqu'il existe des mécanismes common ou d'autres mécanismes d'unification solides, ces possibilités sont sévèrement limitées.

Le cadre ou la bibliothèque que vous avez choisi vous obligent-ils à vous arrêter sur le même choix dans d'autres services?

Certaines bibliothèques sont assez envahissantes. Lorsque vous décidez de les intégrer à des systèmes externes, il peut soudainement s'avérer que votre choix n'est pas si grand lorsque vous allez implémenter un nouveau service dans une pile technologique différente. Les coûts liés à son intégration dans le même esprit que le reste des services peuvent tout simplement être insupportables.

Supposons, par exemple, que vous ayez plusieurs services écrits dans NodeJS qui utilisent activement JSON: API . Ensuite, vous voulez implémenter un nouveau service sur Scala et il semble qu'il n'y ait pas de bibliothèque cliente appropriée (il y a quelques années, nous avions un cas similaire, mais maintenant la situation dans ce domaine s'est légèrement améliorée).

La défaillance d'un seul service entraîne-t-elle une défaillance de l'ensemble du système?

Supposons que la communication dans votre système soit complètement synchrone. Un service fait une demande à un autre et attend une réponse de sa part. Si le premier service échoue, le deuxième service ne pourra pas fonctionner du tout. Si tel est le cas dans votre système, essayez d'y implémenter une communication asynchrone basée sur les messages. Il peut être un peu plus difficile de simuler tous les processus, mais cette approche permet de lisser les effets des pannes.

Dans les cas où cela n'est pas possible, vous pouvez essayer de limiter temporairement l'ensemble des fonctionnalités de l'application. Par exemple, sur la page de la boutique en ligne affiche des informations sur le produit et des avis sur celui-ci, en outre, les informations sur le produit et les avis proviennent de deux sources différentes. Si le microservice d'avis ne fonctionne pas, vous pouvez toujours afficher la page du produit en informant simplement l'utilisateur que le système d'avis est temporairement indisponible.

Y a-t-il un composant partagé sur votre système?

Avez-vous partagé du code? Configuration? Quelque chose? Oui, c'est une question non triviale. Les partisans de la propreté des microservices pensent qu'il est préférable de ne pas autoriser du tout les entités partagées dans le système! Il vaut mieux simplement copier que fractionner le code. En pratique, il est parfois considéré comme acceptable d'avoir plusieurs bibliothèques partagées , mais avec de strictes limitations. Il s'agira peut-être de bibliothèques contenant des fichiers proto pour les tampons de protocole ou simplement des objets POJO ordinaires. Mieux vaut éviter les dépendances dans de telles bibliothèques. Nous avons eu un cas où une simple bibliothèque cliente qui provoquait des conflits de dépendance dans un grand système. À un moment donné, il a semblé que les éléments les plus anciens de l'application utilisaient l'ancienne bibliothèque HTTP, qui n'a pas pu être mise à jour. Heureusement, ces situations peuvent être évitées en utilisant le renommage des dépendances dans le processus de construction (ombrage des dépendances), mais soyez très prudent.

Le préjudice causé par la sur-cohésion des services est beaucoup plus préjudiciable aux problèmes causés par la répétabilité du code.

Sam Newman, Création de microservices

Conclusions

La mise en œuvre du parfait système basé sur des microservices n'est pas une tâche facile. Les gens ont tendance à «rogner», à introduire des liens étroits dans le système découlant du code partagé, et à un moment donné, un tel système se transforme en pratique en un monolithe distribué. Cela se produit souvent si les microservices commencent à être déjà utilisés au début du projet, lorsque le sujet n'est toujours pas bien étudié et que vous n'avez pas une solide expérience prête à l'emploi dans DevOps. Il est préférable de commencer avec un monolithe correctement structuré , où tous les domaines de responsabilité sont parfaitement connus et peuvent être facilement séparés les uns des autres. Cependant, dès le début, vous devez vous assurer que votre système est conçu proprement et que l'organisation du package est soigneusement suivie, ce qui vous permettra par la suite de migrer facilement le code vers de nouveaux services (par exemple, un seul package «presque de niveau supérieur» peut être la base d'un nouveau microservice). . ArchUnit peut aider à l'analyse des dépendances qui surviennent entre les packages.

(...) vous ne devriez pas commencer immédiatement à travailler sur un nouveau projet avec l'introduction de microservices, même si vous êtes sûr que votre application sera suffisamment grande pour que les microservices se justifient.
martinfowler.com/bliki/MonolithFirst.html

N'oubliez pas que les microservices devraient simplifier le développement et le support! Tout ce dont vous avez besoin, c'est de services à couplage lâche, chacun pouvant être déployé indépendamment des autres.

Source: https://habr.com/ru/post/fr478590/


All Articles