Quelle est la bonne façon de synchroniser les données entre les microservices?

19

Je suis relativement nouveau dans l'architecture de microservices. Nous avons une application Web de taille moyenne et je pèse les avantages et les inconvénients de la décomposer en microservices au lieu d'un système monolithique que nous avons maintenant aller de l'avant.

Pour autant que je le comprends, considérons les microservices Aet Bchacun d'eux s'appuie sur un sous-ensemble de données que l'autre possède. Si un message est publié en Adisant que quelque chose a changé, Bpeut consommer ce message et répliquer une copie locale des Ainformations de et l'utiliser pour faire tout ce qui Bdoit être fait.

Cependant, que se Bpasse- t-il si descend / échoue et après un certain temps, revient à nouveau. Pendant ce temps d'arrêt, Aa publié deux autres messages. Comment Bsait comment mettre à jour sa copie locale des Ainformations de?

Certes, si Best le seul consommateur de Ala file d'attente de, alors il peut commencer à le lire une fois qu'il sera de retour en ligne, mais que se passe-t-il s'il y a d'autres consommateurs de cette file d'attente et que ces messages sont consommés?

À titre d'exemple plus concret, si un Usersservice a son adresse e-mail mise à jour alors qu'un Billingmicroservice est en panne, si le Billingmicroservice réapparaît, comment sait-il que l'e-mail a été mis à jour?

Lorsque les microservices reviennent, est-ce que cela fait une émission disant "Hey, je suis de retour, donnez-moi toutes vos informations actuelles?"

En général, quelles seraient les meilleures pratiques de l'industrie pour la synchronisation des données?

noblerare
la source
1
Pour l'éviter autant que possible.
Telastyn
1
Pourquoi Ordersfaut-il savoir quelque chose Users?
kdgregory
Ce n'est qu'un exemple. Remplacez les deux par ce que vous voulez et qui a du sens.
noblerare
un routage de fan out résoudra votre problème «le message est consommé par quelqu'un d'autre». mais ce n'est vraiment pas clair ce que vous essayez d'atteindre.
Ewan
@Ewan J'ai mis à jour mon message d'origine pour mieux expliquer ce que j'essaie de demander.
noblerare

Réponses:

5

Je contesterais toute votre idée de "pousser les données vers tous les autres microservices".

Habituellement, si un service de facturation a besoin d'une adresse e-mail, il demande simplement au service d'adresse l'adresse e-mail du client spécifique. Il n'a pas besoin de conserver une copie de toutes les données d'adresse et ne sera pas informé en cas de changement. Il demande simplement et obtient la réponse des données les plus récentes.

J. Fabian Meier
la source
Je pense que cette réponse est tout à fait juste. Il élimine de nombreux problèmes liés à la synchronisation. En fait, je regarde en ce moment du code qui a de tels problèmes parce que différents services conservent des copies d'informations et ont de tels problèmes de synchronisation.
DaveG
2
Merci pour votre réponse. Alors pourquoi y a-t-il un besoin d'un modèle pub / sub et de files d'attente de messages? Si nous essayons de «tirer» au lieu de «pousser» les données, nous nous inquiétons de la latence du service.
noblerare
AFAIK, votre service n'a pas besoin de réagir immédiatement si quelque chose change (comme dans un pub / sub), mais a parfois besoin de données. Ensuite, je le tirais. Si vous vous inquiétez de la latence, vous pouvez mettre en cache les données, mais cela se fait à nouveau au prix de ne pas savoir si les données sont à jour. Si vos fichiers sont volumineux, vous pouvez également demander si quelque chose change avant de retirer quelque chose.
J.Fabian Meier
Gardez à l'esprit que cette solution a un coût de couplage étroit du service dépendant, ce qui signifie que l'adresse e-mail sera indisponible lorsque le service utilisateur n'est pas disponible. L'une des idées initiales de répartir les services pour qu'ils soient déployables, évolutifs, etc. de manière indépendante. Si tous les services communiquaient directement entre eux sans cache ni garantie de haute disponibilité, alors lorsqu'un système est en panne, ils ont tous descendre.
dukethrash
@dukethrash Ensuite, rendez-les hautement disponibles.
J.Fabian Meier
5

Après avoir fait un peu plus de recherche, je suis tombé sur cet article dont j'ai tiré quelques citations qui je pense sont utiles pour ce que je veux accomplir (et pour tous les futurs lecteurs). Cela offre un moyen d'adopter un modèle de programmation réactive par rapport à un modèle de programmation impératif.

Recherche d'événements

L'idée ici est de représenter la transition d'état de chaque application sous la forme d'un événement immuable. Les événements sont ensuite stockés sous forme de journal ou de journal au fur et à mesure qu'ils se produisent (également appelés «magasin d'événements»). Ils peuvent également être interrogés et stockés indéfiniment, dans le but de représenter comment l'état de l'application, dans son ensemble, a évolué au fil du temps.

Ce que cela permet d'accomplir, c'est que si un microservice tombe en panne et que d'autres événements qui le concernent sont publiés et que les événements sont consommés par, par exemple, d'autres instances de ce microservice, lorsque ce microservice revient, il peut s'y référer event storepour récupérer tous les événements qu'il a manqués au cours de la période où il est tombé.

Apache Kafka en tant que courtier d'événements

Envisagez l'utilisation d'Apache Kafka qui peut stocker et distribuer des milliers d'événements par seconde et dispose de mécanismes de réplication et de tolérance aux pannes intégrés. Il a une mémoire persistante d'événements qui peuvent être stockés sur le disque indéfiniment et consommés à tout moment (mais non supprimés) du sujet (la file d'attente de fantaisie de Kafka) ont été livrés à.

Les événements sont ensuite affectés à des décalages qui les identifient de manière univoque dans le sujet - Kafka peut gérer les décalages lui-même, fournissant facilement une sémantique de livraison «au plus une fois» ou «au moins une fois», mais ils peuvent également être négociés lorsqu'un consommateur d'événements rejoint un sujet. , permettant aux microservices de commencer à consommer des événements à partir de n'importe quel endroit arbitraire dans le temps - généralement là où le consommateur s'est arrêté. Si le dernier décalage d'événement consommé persiste de manière transactionnelle dans le stockage local des services lorsque les cas d'utilisation se terminent avec succès, ce décalage peut facilement être utilisé pour obtenir une sémantique de livraison d'événement «une seule fois».

En fait, lorsque les consommateurs s'identifieront à Kafka, Kafka enregistrera quels messages ont été livrés à quel consommateur afin qu'il ne les réutilise plus.

Sagas

Pour les cas d'utilisation plus complexes où la communication entre différents services est en effet nécessaire, la responsabilité de terminer le cas d'utilisation doit être bien reconnue - le cas d'utilisation est décentralisé et ne se termine que lorsque tous les services impliqués reconnaissent que leur tâche a été menée à bien, sinon l'ensemble du cas d'utilisation doit échouer. et des mesures correctives doivent être déclenchées pour annuler tout état local non valide.

C'est alors que la saga entre en jeu. Une saga est une séquence de transactions locales. Chaque transaction locale met à jour la base de données et publie un message ou un événement pour déclencher la prochaine transaction locale dans la saga. Si une transaction locale échoue car elle enfreint une règle commerciale, la saga exécute une série de transactions compensatoires qui annulent les modifications apportées par les transactions locales précédentes. Lisez ceci pour plus d'informations.

noblerare
la source
Je ne comprends toujours pas pourquoi vous voulez construire une structure aussi compliquée. Il est généralement beaucoup plus facile si chaque service détient uniquement ses propres données et les transmet à d'autres services sur demande.
J.Fabian Meier
^ Mais cela réduira la disponibilité du système. La structure compliquée peut être justifiée si une haute résilience est requise.
avmohan
1

Même si je suis en retard, je voudrais mettre mes 2 cents sur l'argument car je pense que c'est un point important lorsque vous voulez évaluer la conception d'une architecture de microservices événementielle. Chaque microservice sait exactement quels sont les événements qui ont un impact sur son état et peut les attendre. Lorsque le microservice n'est pas disponible, il doit y avoir un composant qui conserve les messages nécessaires du microservice défaillant jusqu'à ce qu'il ne soit pas en mesure de les «consommer». Il s'agit en fait d'un modèle "producteur / consommateur" et non d'un modèle "publier / souscrire". Les courtiers de messages (comme Kafka, RabbitMQ, ActiveMQ, etc.) sont généralement le meilleur moyen d'obtenir ce comportement (à moins que vous n'implémentiez pas quelque chose de différent comme le sourcing d'événements) en fournissant des files d'attente persistantes et un mécanisme ack / nack.

Maintenant, le microservice sait qu'un message est finalement délivré, mais ce n'est pas suffisant: quelle est la façon dont il attend la livraison d'un seul message? peut-il gérer la livraison de plusieurs copies de la même notification d'événement? C'est une question de livraison sémantique (au moins une fois, exactement une fois)

Dernières pensées):

  1. Lorsque vous ajoutez un microservice à votre architecture qui doit consommer les événements des autres, vous devez effectuer la première synchronisation

  2. Même le courtier peut échouer, dans ce cas, les messages sont perdus

pour les deux scénarios, il serait utile de disposer de mécanismes simples pour réhydrater votre état de microservice. Il peut s'agir d'une API REST ou d'un script qui envoie des messages, mais le plus important est d'avoir des moyens d'effectuer une tâche de maintenance

Carmine Ingaldi
la source
0

Vous pouvez remplacer une file d'attente d'événements normale par un modèle éditeur / abonné, dans lequel le Aservice publie un nouveau message de la rubrique T et le Btype de microservices s'abonnerait à la même rubrique.

Idéalement, ce Bserait un service sans état, et il utiliserait un service de persistance détaché, de sorte qu'une Binstance de service défaillante serait remplacée par la création d'une ou plusieurs Binstances de service pour continuer son travail, en lisant à partir du même service de persistance partagé.

A.Rashad
la source
0

Si un message est publié par A disant que quelque chose a changé, B peut consommer ce message et répliquer une copie locale des informations de A et l'utiliser pour faire tout ce que B doit faire.

Si vous vouliez que B puisse accéder aux données internes de A, vous feriez mieux de lui donner simplement accès aux bases de données internes de A.

Cependant, vous ne devriez pas faire cela, le point essentiel d'une architecture orientée service est que le service B ne peut pas voir l'état interne du service A et est limité à effectuer des requêtes via les API REST (et vice versa).

Dans votre cas, vous pourriez avoir un service de données utilisateur, qui a la responsabilité de stocker toutes les données utilisateur. D'autres services qui souhaitent utiliser ces données ne le demandent que lorsqu'ils en ont besoin et n'en conservent pas de copie locale (ce qui est vraiment utile si vous pensez à la conformité au RGPD). Le service de données utilisateur peut prendre en charge des opérations CRUD simples comme «Créer un nouvel utilisateur» ou «Changer le nom pour user_id 23» ou il peut avoir des opérations plus complexes, «Trouver tous les utilisateurs standard avec un anniversaire à venir dans les 2 prochaines semaines et leur donner statut d'essai premium ". Désormais, lorsque votre service de facturation doit envoyer un e-mail à l'utilisateur 42, il demande au service de données utilisateur "Quelle est l'adresse e-mail de user_id 42", utilise ses données internes avec toutes les informations de facturation pour créer l'e-mail, puis peut transmettre le message adresse e-mail et corps d'un serveur de messagerie.

Helena
la source