web-dev-qa-db-fra.com

Quelle est la stratégie de transaction la plus acceptée pour les microservices

L'un des problèmes majeurs que j'ai vus se produire dans un système avec des microservices est la façon dont les transactions fonctionnent lorsqu'elles s'étendent sur différents services. Dans notre propre architecture, nous avons utilisé des transactions distribuées pour résoudre ce problème, mais elles comportent leurs propres problèmes. Surtout, les blocages ont été une douleur jusqu'à présent.

Une autre option semble être une sorte de gestionnaire de transactions sur mesure, qui connaît les flux au sein de votre système et prendra en charge les restaurations pour vous en tant que processus d'arrière-plan couvrant l'ensemble de votre système (il indiquera donc aux autres services de restaurer et s'ils sont en panne, prévenez-les plus tard).

Y a-t-il une autre option acceptée? Les deux semblent avoir leurs inconvénients. Le premier pourrait provoquer des blocages et un tas d'autres problèmes, le second pourrait entraîner une incohérence des données. Y a-t-il de meilleures options?

88
Kristof

L'approche habituelle consiste à isoler autant que possible ces microservices - à les traiter comme des unités uniques. Ensuite, les transactions peuvent être développées dans le contexte du service dans son ensemble (c'est-à-dire ne faisant pas partie des transactions DB habituelles, bien que vous puissiez toujours avoir des transactions DB internes au service).

Réfléchissez à la façon dont les transactions se produisent et à quel type de sens pour vos services.Vous pouvez alors implémenter un mécanisme de restauration qui annule l'opération d'origine, ou un système de validation en deux phases qui réserve l'opération d'origine jusqu'à ce qu'il vous soit dit de valider pour de vrai. Bien sûr, ces deux systèmes signifient que vous implémentez le vôtre, mais vous implémentez déjà vos microservices.

Les services financiers font ce genre de chose tout le temps - si je veux transférer de l'argent de ma banque à votre banque, il n'y a pas de transaction unique comme vous le feriez dans une BD. Vous ne savez pas quels systèmes l'une ou l'autre des banques exécute, vous devez donc les traiter efficacement comme vos microservices. Dans ce cas, ma banque déplacerait mon argent de mon compte vers un compte de dépôt, puis dirait à votre banque qu'elle a de l'argent.Si l'envoi échoue, ma banque remboursera mon compte avec l'argent qu'elle a essayé d'envoyer.

43
gbjbaanb

Je pense que la sagesse standard est de ne jamais faire transiger les transactions par-delà les frontières des microservices. Si un ensemble de données donné doit vraiment être atomiquement cohérent avec un autre, ces deux choses vont de pair.

C'est l'une des raisons pour lesquelles il est très difficile de diviser un système en services jusqu'à ce que vous l'ayez entièrement conçu. Ce qui dans le monde moderne signifie probablement écrit ...

31
soru

Je pense que si la cohérence est une exigence forte dans votre application, vous devriez vous demander si les microservices sont la meilleure approche. Comme Martin Fowler dit :

Les microservices introduisent d'éventuels problèmes de cohérence en raison de leur insistance louable sur la gestion décentralisée des données. Avec un monolith, vous pouvez mettre à jour un tas de choses ensemble en une seule transaction. Les microservices nécessitent plusieurs ressources pour être mis à jour et les transactions distribuées sont désapprouvées (pour une bonne raison). Alors maintenant, les développeurs doivent être conscients des problèmes de cohérence et trouver comment détecter quand les choses ne sont pas synchronisées avant de faire quoi que ce soit que le code regrette.

Mais peut-être que dans votre cas, vous pouvez sacrifier la cohérence en termes de disponibilité

Les processus métier sont souvent plus tolérants aux incohérences que vous ne le pensez car les entreprises accordent souvent plus d'importance à la disponibilité.

Cependant, je me demande également s'il existe une stratégie pour les transactions distribuées dans les microservices, mais peut-être que le coût est trop élevé. Je voulais vous donner mes deux cents avec l'article toujours excellent de Martin Fowler et le théorème CAP .

17
gabrielgiussi

Comme suggéré dans au moins une des réponses ici mais aussi ailleurs sur le web, il est possible de concevoir un microservice qui conserve les entités ensemble dans une transaction normale si vous avez besoin de cohérence entre les deux entités.

Mais en même temps, vous pourriez bien avoir la situation où les entités n'appartiennent vraiment pas au même microservice, par exemple, les enregistrements de vente et les enregistrements de commande (lorsque vous commandez quelque chose pour réaliser la vente). Dans de tels cas, vous pourriez bien avoir besoin d'un moyen d'assurer la cohérence entre les deux microservices.

Les transactions distribuées traditionnellement ont été utilisées et d'après mon expérience, elles fonctionnent bien jusqu'à ce qu'elles évoluent à une taille où le verrouillage devient un problème. Vous pouvez assouplir le verrouillage afin que seules les ressources pertinentes (par exemple, l'article vendu) soient "verrouillées" à l'aide d'un changement d'état, mais c'est là que cela commence à devenir délicat car vous entrez sur le territoire où vous devez construire tous les logique de le faire vous-même, plutôt que d'avoir, disons, une base de données qui le gère pour vous.

J'ai travaillé avec des entreprises qui ont choisi de créer leur propre cadre de transactions pour gérer ce problème complexe, mais je ne le recommande pas car il est coûteux et prend du temps à mûrir.

Il existe des produits qui peuvent être boulonnés sur votre système et qui veillent à la cohérence. Un moteur de processus métier en est un bon exemple et il gère généralement la cohérence éventuellement et en utilisant la compensation. D'autres produits fonctionnent de manière similaire. Vous vous retrouvez généralement avec une couche de logiciels à proximité du (des) client (s), qui traite de la cohérence et des transactions et appelle les (micro) services pour effectuer le traitement commercial réel . Un de ces produits est un connecteur JCA générique qui peut être utilisé avec Java EE (pour plus de transparence: je suis l'auteur). Voir http://blog.maxant.co .uk/pebble/2015/08/04/1438716480000.html pour plus de détails et une discussion plus approfondie des questions soulevées ici.

Une autre façon de gérer les transactions et la cohérence consiste à encapsuler un appel à un microservice dans un appel à quelque chose de transactionnel comme une file d'attente de messages. Prenez l'exemple d'enregistrement des ventes/enregistrement des commandes ci-dessus - vous pouvez simplement laisser le microservice des ventes envoyer un message au système de commande, qui est engagé dans la même transaction qui écrit la vente dans la base de données. Le résultat est une solution asynchrone qui évolue bien très. En utilisant des technologies telles que les sockets Web, vous pouvez même contourner le problème du blocage qui est souvent lié à l'extension des solutions asynchrones. Pour plus d'idées sur des modèles comme celui-ci, consultez un autre de mes articles: http://blog.maxant.co.uk/pebble/2015/08/11/1439322480000.html .

Quelle que soit la solution que vous choisissez, il est important de reconnaître que seule une petite partie de votre système écrit des éléments qui doivent être cohérents - la plupart des accès sont susceptibles d'être en lecture seule. Pour cette raison, n'intégrez la gestion des transactions que dans les parties pertinentes du système, afin qu'elle puisse toujours bien évoluer.

16
Ant Kutschera

Il existe de nombreuses solutions qui compromettent plus que je ne suis à l'aise. Certes, si votre cas d'utilisation est complexe, comme le transfert d'argent entre différentes banques, des alternatives plus agréables peuvent être impossibles. Mais regardons ce que nous pouvons faire dans le scénario courant, où l'utilisation de microservices interfère avec nos transactions de bases de données potentielles.

Option 1: Évitez le besoin de transactions si possible

Évident et mentionné précédemment, mais idéal si nous pouvons le gérer. Les composants appartenaient-ils réellement au même microservice? Ou pouvons-nous repenser le (s) système (s) de sorte que la transaction devienne inutile? Accepter la non-transactionnalité est peut-être le sacrifice le plus abordable.

Option 2: utiliser une file d'attente

S'il y a suffisamment de certitude que l'autre service sera réussi à tout ce que nous voulons qu'il fasse, nous pouvons l'appeler via une forme de file d'attente. L'élément en file d'attente ne sera récupéré que plus tard, mais nous pouvons nous assurer que l'élément est mis en file d'attente.

Par exemple, disons que nous voulons insérer une entité et envoyer un e-mail, comme une seule transaction. Au lieu d'appeler le serveur de messagerie, nous mettons en file d'attente l'e-mail dans une table.

Begin transaction
Insert entity
Insert e-mail
Commit transaction

Un inconvénient évident est que plusieurs microservices auront besoin d'accéder à la même table.

Option 3: effectuez le travail externe en dernier, juste avant de terminer la transaction

Cette approche repose sur l'hypothèse qu'il est très peu probable que la validation de la transaction échoue.

Begin transaction
Insert entity
Insert another entity
Make external call
Commit transaction

Si les requêtes échouent, l'appel externe n'a pas encore eu lieu. Si l'appel externe échoue, la transaction n'est jamais validée.

Cette approche comporte les limites que nous ne pouvons faire que un appel externe, et cela doit être fait en dernier (c'est-à-dire que nous ne pouvons pas utiliser son résultat dans nos requêtes).

Option 4: créer des choses dans un état en attente

Tel que publié ici , nous pouvons avoir plusieurs microservices créant différents composants, chacun dans un état en attente, sans transaction.

Toute validation est effectuée, mais rien n'est créé dans un état définitif. Une fois que tout a été créé avec succès, chaque composant est activé. Habituellement, cette opération est si simple et les chances de quelque chose qui ne va pas sont si petites que nous pouvons même préférer effectuer l'activation de manière non transactionnelle.

Le plus grand inconvénient est probablement que nous devons tenir compte de l’existence d’articles en attente. Toute requête de sélection doit déterminer s'il faut inclure les données en attente. La plupart devraient l'ignorer. Et les mises à jour sont une autre histoire.

Option 5: laisser le microservice partager sa requête

Aucune des autres options ne le fait pour vous? Obtenons alors peu orthodoxe.

Selon l'entreprise, celle-ci peut être inacceptable. Je suis au courant. C'est peu orthodoxe. Si ce n'est pas acceptable, suivez une autre voie. Mais si cela correspond à votre situation, cela résout le problème simplement et puissamment. Ce pourrait être le compromis le plus acceptable.

Il existe un moyen de transformer les requêtes de plusieurs microservices en une transaction de base de données simple et unique.

Retourne la requête, plutôt que de l'exécuter.

Begin transaction
Execute our own query
Make external call, receiving a query
Execute received query
Commit transaction

Au niveau du réseau, chaque microservice doit pouvoir accéder à chaque base de données. Gardez cela à l'esprit, également en ce qui concerne la mise à l'échelle future.

Si les bases de données impliquées dans la transaction sont sur le même serveur, ce sera une transaction régulière. S'ils se trouvent sur des serveurs différents, ce sera une transaction distribuée. Le code est le même malgré tout.

Nous recevons la requête, y compris son type de connexion, ses paramètres et sa chaîne de connexion. Nous pouvons l'envelopper dans une classe de commande exécutable soignée, en gardant le flux lisible: l'appel de microservice aboutit à une commande, que nous exécutons, dans le cadre de notre transaction.

La chaîne de connexion est ce que le microservice d'origine nous donne, donc à toutes fins utiles, la requête est toujours considérée comme exécutée par ce microservice. Nous le routons simplement physiquement via le microservice client. Cela fait-il une différence? Eh bien, cela nous permet de le mettre dans la même transaction avec une autre requête.

Si le compromis est acceptable, cette approche nous donne la transactionnalité simple d'une application monolithique, dans une architecture de microservice.

2
Timo

Dans les microservices, il existe trois façons d'obtenir une cohérence entre diff. prestations de service:

  1. Orchestration - Un processus qui gère la transaction et la restauration entre les services.

  2. Chorégraphie - Le service passe des messages entre eux et atteint finalement un état cohérent.

  3. Hybride - Mélanger les deux ci-dessus.

Pour une lecture complète, accédez au lien: https://medium.com/capital-one-developers/microservices-when-to-react-vs-orchestrate-c6b18308a14c

1
techagrammer

Je commencerais par décomposer l'espace problématique - identifier vos limites de service . Une fois fait correctement, vous n'auriez jamais besoin d'avoir des transactions entre les services.

Différents services ont leurs propres données, comportements, forces de motivation, gouvernement, règles commerciales, etc. Un bon début est de répertorier les capacités de haut niveau de votre entreprise. Par exemple, marketing, ventes, comptabilité, support. Un autre point de départ est la structure organisationnelle, mais sachez qu'il y a une mise en garde - pour certaines raisons (politiques, par exemple), ce n'est peut-être pas le schéma optimal de décomposition des entreprises. Une approche plus stricte est analyse de la chaîne de valeur . N'oubliez pas que vos services peuvent également inclure des personnes, ce n'est pas strictement un logiciel. Les services doivent communiquer entre eux via événements .

La prochaine étape consiste à sculpter ces services. En conséquence, vous obtenez toujours relativement indépendant agrégats . Ils représentent une unité de cohérence. En d'autres termes, leurs éléments internes doivent être cohérents et ACIDES. Les agrégats communiquent entre eux via des événements.

Si vous pensez que votre domaine exige d'abord de la cohérence, détrompez-vous. Aucun des grands systèmes stratégiques n'est conçu dans cet esprit. Ils sont tous distribués et finalement cohérents. Vérifiez Pat Helland's papier classique .

Ici sont quelques conseils pratiques sur la façon de construire un système distribué.

0
Zapadlo