Problèmes de code courants dans les microservices

Bonjour à tous!

Récemment, lors d'une conférence PGConf à Moscou, l'un des intervenants a démontré une architecture de «microservice», mentionnant au passage que tous les microservices héritent d'une classe de base commune. Bien qu'il n'y ait eu aucune explication pour la mise en œuvre, il semblait que dans cette entreprise le terme «microservices» n'était pas compris de la même manière que les classiques semblaient nous l'enseigner. Aujourd'hui, nous allons traiter l'un des problèmes intéressants - quel peut être le code commun dans les microservices et s'il peut l'être.

Qu'est-ce qu'un microservice? Il s'agit d'une application autonome. Pas un module, pas un processus, pas quelque chose qui est simplement déployé séparément, mais une application à part entière, réelle et séparée. Il a sa propre fonction principale, son propre référentiel dans le git, ses propres tests, sa propre API, son propre serveur web, son propre fichier README, sa propre base de données, sa propre version, ses propres développeurs.

Comme les conteneurs, les microservices ont commencé à être utilisés lorsque la puissance de calcul du matériel et la fiabilité des réseaux ont atteint un niveau tel que vous pouvez vous permettre un appel de fonction qui dure 100 fois plus longtemps qu'auparavant, vous pouvez vous permettre une consommation de mémoire 100 fois plus élevée, vous pouvez vous offrir le luxe pour installer chaque "grand-mère" non seulement dans un "appartement" séparé, mais dans une "maison" séparée. Comme toute solution architecturale, l'architecture des microservices sacrifie une fois de plus les performances, gagnant en maintenabilité du code pour les développeurs. Mais puisque la personne et la rapidité de sa réaction sont restées les mêmes, les systèmes continuent de répondre aux exigences.

Pourquoi se diviser en applications distinctes? Parce que nous distribuons une partie de la complexité du système déjà au niveau de l'architecture système. Le processus de programmation est généralement une «morsure» progressive de la grande «pièce de complexité» initiale, et la décomposition (en classes, modules, fonctions, et dans notre cas, des applications entières) est la mise en œuvre d'une partie de cette complexité sous la forme d'une structure. Lorsque nous avons divisé le système en microservices, nous avons pris une décision architecturale (réussie ou non), que les développeurs n'ont plus besoin de prendre à l'avenir lors de la mise en œuvre de parties spécifiques de la fonctionnalité. Il est connu que ce microservice particulier est responsable de l'envoi d'e-mails, et celui-ci - pour l'autorisation, a déjà été établi, donc toutes mes nouvelles fonctionnalités «tombent» sur ce modèle sans discussion.

Un aspect clé des microservices est une mauvaise connectivité. Les microservices doivent être indépendants du mot «complètement». Ils n'ont pas de structures de données communes et chaque microservice peut / devrait avoir sa propre architecture, technologie, méthode d'assemblage (et ainsi de suite). Par définition. Parce que c'est une application indépendante. Les modifications du code d'un microservice ne doivent en aucun cas affecter les autres, sauf si l'API est affectée. Si j'ai N microservices écrits en Java, il ne devrait pas y avoir de facteurs contraignants pour ne pas écrire le N + 1er microservice en Python, si cela est soudainement rentable pour une raison quelconque. Ils sont faiblement couplés, et donc un développeur qui commence à travailler avec un microservice spécifique:

a) surveille de manière très sensible son API, car c'est le seul composant visible de l'extérieur;
b) se sent complètement libre lors de la refactorisation;
c) Comprendre le but du microservice (ici nous nous souvenons de SRP) et implémente une nouvelle fonction en conséquence;
d) sélectionne la méthode de persistance la plus appropriée;
etc.

Tout cela est bon et semble logique et harmonieux, comme beaucoup d'idéologies et de théories (et ici le théoricien idéologique met fin et va dîner), mais nous pratiquons. Le code n'a pas à être écrit sur martinfowler.com . Et tôt ou tard, nous sommes confrontés au fait que tous les microservices:

  • informations de journal;
  • contenir une autorisation;
  • Accès aux courtiers de messages
  • renvoyer les messages d'erreur corrects;
  • doit en quelque sorte comprendre les entités générales du système, le cas échéant;
  • doit fonctionner avec un format de message (et un protocole) communs;

et faites-le à l'identique.

Et à un moment donné, l'architecte idéologique vient travailler le matin et découvre qu'une «bibliothèque» est apparue dans le système la nuit - un nouveau référentiel avec un code commun qui est utilisé dans de nombreux microservices. Faut-il horrifier un architecte?

Ça dépend.

Pour évaluer correctement la situation, nous devons revenir à l'idée principale: les microservices sont une collection d'applications indépendantes qui interagissent entre elles via une API (réseau). En cela, nous voyons le principal avantage et la simplicité de l'architecture. Et nous ne voulons en aucun cas perdre cet avantage. Le code général qui a été placé dans la «bibliothèque» interfère-t-il avec cela? Regardons quelques exemples.

1. La classe d'utilisateurs (ou une autre entité commerciale) réside dans la bibliothèque.

  • c'est-à-dire une entité commerciale n'est pas encapsulée dans un microservice, mais répartie différemment (sinon pourquoi la placer dans une bibliothèque de code partagée?);
  • c'est-à-dire les microservices se connectent via cette entité commerciale; la modification de la logique de travail avec une entité affectera plusieurs microservices;
  • c'est mauvais, très mauvais, ce n'est pas du tout des microservices, bien que ce ne soit pas une "grosse boule de boue", mais très rapidement la vision du monde de l'équipe conduira à une "grosse boule de boue distribuée";
  • mais les microservices du système fonctionnent avec les mêmes concepts, et les concepts sont souvent des entités, ou simplement des structures avec des champs, que dois-je faire? lire DDD, c'est exactement comment encapsuler des entités à l'intérieur de microservices afin qu'elles ne "survolent" pas l'API.

Malheureusement, toute logique métier placée dans une bibliothèque partagée aura un tel effet. Les bibliothèques de code générales ont tendance à se développer, entraînant au milieu du système une «tumeur» logique qui n'appartient à aucun microservice particulier, et l'architecture se bloque. Le «centre de gravité logique» du système commence à évoluer vers un référentiel avec un code commun, et nous obtenons un mélange infernal de monolithes et de microservices, et nous n'avons pas besoin d'y aller du tout.

2. Le code d'analyse du format de message est placé dans la bibliothèque.

  • Le code est très probablement en Java si tous les microservices sont écrits en Java;
  • Si demain j'écris un service en Python, je ne pourrai pas utiliser l'analyseur, mais il semble que ce ne soit pas un problème du tout, j'écrirai une version Python;
  • Point clé: si j'écris un nouveau microservice en Java, dois-je utiliser cet analyseur? Oui, probablement pas. Je ne suis peut-être pas obligé, même si, en tant que développeur de microservices, il peut être très utile. Eh bien, comme si j'avais trouvé quelque chose d'utile dans le référentiel Maven.

Un analyseur de messages, ou un enregistreur amélioré, ou un client encapsulé pour envoyer des données à RabbitMQ - c'est un peu comme des assistants, des composants auxiliaires. Ils sont à égalité avec les bibliothèques standard de NuGet, Maven ou NPM. Le développeur de microservices est toujours le roi; il décide d'utiliser la bibliothèque standard, ou de créer son propre nouveau code, ou d'utiliser le code de la bibliothèque d'assistance partagée. Comment cela lui sera plus pratique, car il écrit une APP SÉPARÉE ET INDÉPENDANTE. Un assistant particulier peut-il évoluer? Peut-être qu'il aura probablement des versions. Laissez le développeur se référer à une version spécifique de son service, personne ne l'oblige à mettre à jour le service, lors de la mise à jour des assistants, il s'agit de savoir qui prend en charge le service.

3. Interface Java, classe de base abstraite, trait.

  • Ou un autre morceau de la catégorie des "morceaux de code déchirés";
  • C'est-à-dire Je suis ici, indépendant et indépendant, et un morceau de mon foie se trouve ailleurs;
  • Ici, la cohérence des microservices au niveau du code apparaît, nous ne le recommanderons donc pas;
  • Au début, cela ne posera probablement pas de problèmes tangibles, mais l'essence de la conception architecturale est la garantie de confort (ou d'inconfort) pour les années à venir.

L'équipe qui commence à travailler sur un nouveau produit jette les bases de l'architecture et a la plus grande influence sur les tendances du produit. Si les principes de SRP, de décomposition réussie, de faible connectivité, etc. sont initialement incorporés dans le système, alors il a une chance de continuer à se développer correctement. Sinon, alors l'accélération centrifuge des «facteurs temps» (une autre équipe, peu de temps, des correctifs urgents, un manque de documentation) va propulser ce système plus loin qu'il ne semble.

La question d'un code commun dans les microservices reste difficile car elle est associée à une sorte de compromis: nous pesons ce qui sera le plus rentable à l'avenir - le degré d'indépendance des microservices, moins de répétitions dans le code, les qualifications des ingénieurs, la simplicité du système, etc. Ce sont à chaque fois des réflexions et des discussions qui peuvent conduire à différentes décisions architecturales spécifiques. Néanmoins, résumons certaines des recommandations:

Recommandation 0: N'appelez microservices aucune chose qui est divisée en morceaux existants indépendamment. Toutes les tables avec des colonnes ne sont pas une matrice, utilisons les termes correctement.

Recommandation 1: Il est hautement souhaitable que les microservices n'aient aucun code commun du tout.

Recommandation 2: S'il existe encore un code commun, qu'il s'agisse d'une collection (bibliothèque) d '«assistants» facultatifs. Le développeur du service décide de les utiliser ou d'écrire son propre code.

Recommandation 3: En aucun cas, il ne devrait y avoir de logique métier dans le code commun. Toute la logique métier est encapsulée dans des microservices.

Recommandation 4: Laissez la bibliothèque de code commune être conçue comme un package standard (NuGet, Maven, NPM, etc.), avec l'option de versioning (ou, mieux encore, plusieurs packages distincts).

Recommandation 5: Le «centre de gravité logique» du système devrait toujours rester dans les microservices eux-mêmes, et non dans le code commun.

Recommandation 6: Si vous prévoyez d'écrire au format des microservices, conciliez à l'avance que le code entre eux sera parfois dupliqué. Dans une certaine mesure, notre «instinct SEC» naturel doit être supprimé.

Merci pour votre attention et vos microservices réussis.

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


All Articles