Supposons que nous ayons une fonction qui met à jour le mot de passe d'un utilisateur.
Une fois que le bouton «Mettre à jour le mot de passe» est cliqué, un événement UpdatePasswordEvent est envoyé à une rubrique où 3 autres services sont abonnés:
- Un service qui met à jour le mot de passe de l'utilisateur
- Un service qui met à jour l'historique des mots de passe de l'utilisateur
- Un service qui envoie un e-mail informant l'utilisateur que son mot de passe a été modifié.
D'après ce que j'ai compris de la cohérence éventuelle, tous ces services (consommateurs) recevront l'événement en même temps et les traiteront séparément, ce qui, dans un bon scénario, conduira à la cohérence des données.
Cependant, que se passe-t-il si un service ne parvient pas à traiter l'événement? par exemple, déconnexion soudaine, erreur de base de données, etc. Quel est un bon modèle / pratique pour gérer ces échecs de transaction?
Je pensais créer un RollbackTopic où si un événement ne pouvait pas être traité, un RollbackEvent serait créé dans une rubrique où les "services de restauration" feraient leur travail et rétabliraient les données
Réponses:
Non pas forcément. Comme je l'ai commenté, nous ne pouvons pas annuler un e-mail envoyé, nous avons donc encore besoin d'une sorte de "séquence". IPC sur gestion des données événementiel n'est pas exempte d'orchestation 1 .
Par exemple, l'e-mail ne doit pas être envoyé à moins que les transactions précédentes ne se terminent avec succès et que le service de messagerie n'en obtienne une preuve. 3
Dites bonjour aux erreurs de l'informatique distribuée . Ce sont eux qui compliquent les choses et, comme d'habitude, il n'y a pas de balles d'argent pour y faire face.
Avant de commencer notre voyage à la recherche de l'Arche perdue, nous devons d'abord demander à l'organisation. Souvent, la solution réside dans la façon dont l'organisation fait face à ces problèmes dans le monde réel .
Que fait tout le monde (départements) lorsque certaines données sont manquantes ou incomplètes?
Nous nous rendrons compte que les différents services ont des solutions différentes qui, ensemble, constituent la solution à mettre en œuvre.
Quoi qu'il en soit, voici quelques pratiques qui pourraient nous aider dans la stratégie à suivre.
Éventuelle cohérence
Plutôt que de veiller à ce que le système soit constamment dans un état cohérent, nous pouvons plutôt accepter que le système l'obtiendra à un moment donné dans le futur. Cette approche est particulièrement utile pour les opérations commerciales de longue durée.
La façon dont le système atteint la cohérence varie d'un système à l'autre. Cela peut impliquer des processus automatisés à une sorte d'intervention humaine. Par exemple, le réessayer typique plus tard ou le contact avec le service client .
Annuler toutes les opérations
Remettez le système dans un état cohérent via des transactions compensatoires . Cependant, nous devons tenir compte du fait que ces transactions peuvent également échouer, ce qui pourrait nous conduire à un point où l'incohérence est encore plus difficile à résoudre. Et, encore une fois, nous ne pouvons pas annuler un e-mail envoyé.
Pour un faible nombre de transactions, cette approche est faisable, car le nombre de transactions compensatoires est également faible. S'il y avait plusieurs transactions commerciales impliquées dans la CIB, le traitement d'une transaction compensatoire pour chacune d'entre elles serait difficile.
Si nous optons pour des transactions compensatoires , nous trouverons que le modèle de conception des disjoncteurs est très utile - et obligatoire j'oserais dire -
Transactions distribuées
L'idée est de couvrir plusieurs transactions au sein d'une même transaction, via un processus de gouvernance global appelé Transaction Manager . Un algorithme courant pour gérer les transactions distribuées est la validation en deux phases .
La principale préoccupation des transactions distribuées est qu'elles reposent sur le verrouillage des ressources pendant sa durée de vie, et comme nous le savons, les choses peuvent mal tourner pour le gestionnaire de transactions .
Si les gestionnaires de transactions sont compromis, nous pouvons nous retrouver avec plusieurs verrous dans les différents contextes limités, ce qui entraîne des comportements inattendus en raison de la mise en file d'attente des messages. 2
Opérations de décomposition. Pourquoi?
En accord avec les arguments ci-dessus, Sam - dans son livre Building Microservices - déclare que, si nous ne pouvons vraiment pas vraiment nous permettre la cohérence éventuelle, nous devons éviter de diviser l'opération maintenant.
Si nous ne pouvons pas nous permettre de scinder certaines opérations en deux ou plusieurs transactions, cela pourrait dire que - probablement - ces transactions appartiennent au même contexte délimité, ou - au moins - à un contexte transversal qui reste à modéliser.
Par exemple, dans notre cas, nous nous rendons compte que les transactions # 1 et # 2 sont étroitement liées l'une à l'autre et probablement les deux pourraient appartenir au même contexte délimité Comptes , Utilisateurs , Registre , peu importe ...
Pensez à placer les deux opérations dans les limites de la même transaction. Cela rendrait l'opération plus facile à gérer. Évaluez également le niveau de criticité de chaque transaction. Probablement, si la transaction # 2 échoue, elle ne devrait pas compromettre toute l'opération. En cas de doute, demandez à l' organisation .
1: Pas le genre d'orchestration que vous pensez. Je ne parle pas de l'orchestation d'ESB. Je parle de faire réagir les services à l'événement approprié.
2: Vous pourriez trouver les opinions intéressantes de Sam Newman concernant les transactions distribuées.
3: Consultez la réponse de David Parker à ce sujet.
la source
Dans votre cas, vous ne pouvez pas traiter les trois choses à la fois. Ce dont vous avez besoin, c'est d'un processus. Voici un exemple extrêmement simplifié:
Il est important de savoir que les opérations de modification d'état DOIVENT toujours être effectuées sur une entité cohérente. Sauf si vous pouvez garantir une cohérence élevée , elle doit être faite sur un dossier principal.
Votre système doit garantir qu'avant qu'un événement ne se produise dans votre système, les modifications DOIVENT d'abord être conservées avec la sécurité transactionnelle. C'est pour s'assurer qu'un événement déclenché est vraiment une confirmation de ce qui s'est réellement passé.
Il y a plusieurs parties délicates du processus tel quel et je vais ignorer les plus évidentes - telles que: Que faire si votre serveur de base de données meurt lorsqu'il persiste un utilisateur avec un mot de passe modifié? Vous émettez simplement le UpdatePassword à nouveau. Cependant, certaines parties doivent être prises en charge par vous, et ce sont:
Dans un système, l'orchestrateur de processus (PO) n'est rien d'autre qu'une autre entité, qui contient un état interne - au sens littéral également - et permet des transitions entre les états, agissant effectivement comme une sorte de machine à états. Grâce à l'état interne, vous pouvez supprimer le traitement de duplication des messages.
Lorsque le bon de commande est dans un
New
état et est en cours de traitementUserPasswordHasBeenUpdated
, il change son état enUserPasswordHasBeenUpdated
(ou selon le nom d'état qui vous convient ). Si le bon de commande se trouve toujours dans unUserPasswordHasBeenUpdated
et un autreUserPasswordHasBeenUpdated
arriverait, le bon de commande ignorerait complètement le message, sachant qu'il s'agit d'une duplication. Un mécanisme similaire serait également mis en œuvre pour d'autres États.La gestion de l'envoi réel de l'e-mail est un peu plus délicate. Ici, vous avez deux options:
Envoyez-le au plus une fois
Avec cette option, lorsque le bon de
UserPasswordHistoryHasBeenSaved
commande a atteint l' état, une commande pour envoyer un e-mail est envoyée en réaction au changement d'état. Votre système garantirait laUserPasswordHistoryHasBeenSaved
persistance de l' état avant d'envoyer l'e-mail, c'est-à-dire qu'un message en double ne déclencherait pas l'envoi de l'e-mail à nouveau. Avec cette approche, vous vous assurez que l'état correct est enregistré pour le bon de commande mais ne pouvez garantir aucune opération suivante.Envoyez-le au moins une fois
C'est ce que j'irais.
Au lieu d'enregistrer
UserPasswordHistoryHasBeenSaved
et d'envoyer l'e-mail en réaction, vous essayez d'abord d'envoyer l'e-mail. Si l'opération d'envoi échoue, l'état du bon de commande n'est jamais changé enUserPasswordHistoryHasBeenSaved
et un autre message du même type est toujours traité. Si l'envoi de l'e-mail devait réussir, mais que votre système échouerait pendant la persistance du bon de commande avec son nouvelUserPasswordHistoryHasBeenSaved
état, un autre message duUserPasswordHistoryHasBeenSaved
déclencherait à nouveau la commande pour envoyer l'e-mail et l'utilisateur l'aurait reçu plusieurs fois .Dans votre cas, vous voulez vous assurer que l'utilisateur reçoit bien l'e-mail. C'est pourquoi je choisirais la deuxième option par rapport à la première.
la source
Les systèmes de file d'attente ne sont pas aussi fragiles que vous ne le pensez.
Si nous écrivions les trois processus dans une base de données relationnelle, nous pourrions utiliser une transaction pour gérer une défaillance des processus intermédiaires.
Sans l'engagement final, le travail partiel serait rejeté.
Dans un système de bases de files d'attente, vous aurez des options similaires lorsque vous lirez un message dans la file d'attente pour gérer les échecs de mi-processus.
Amazon SQS, par exemple, masque simplement les messages qui sont lus. sauf si une dernière commande Supprimer est envoyée, le message réapparaît ou est placé dans une file d'attente de lettres mortes.
Vous pouvez implémenter des «transactions» similaires de différentes manières, essentiellement en conservant une copie du message jusqu'à ce que vous receviez la confirmation du succès du traitement. Si la confirmation n'est pas reçue à temps. vous pouvez renvoyer le message ou le conserver pour une attention manuelle.
Vous pourriez potentiellement créer un «service de restauration» qui surveillait ces messages erronés, connaissait les messages associés et l'état passé et effectuait une restauration.
Toutefois! Il est généralement préférable de renvoyer les messages erronés. Après tout, ce sont des cas marginaux. Soit un serveur a échoué de façon catastrophique, soit il y a eu un bogue dans la gestion d'un type de message particulier.
Une fois alerté de l'erreur, le service peut être réparé et les messages traités avec succès. Ramener le système dans son ensemble à un état cohérent.
la source
Ce à quoi vous êtes confronté ici, c'est le problème des deux généraux . Essentiellement: comment être sûr qu'un message est reçu et qu'une réponse à ce message se produit? Dans de nombreux cas, une solution parfaite n'existe pas. En fait, dans un système distribué, il est souvent impossible d'obtenir une livraison exacte des messages.
Une première remarque évidente est que le service qui modifie le mot de passe doit envoyer l'événement de changement de mot de passe. De cette façon, l'historique des mots de passe et les services d'envoi de courrier ne sont déclenchés que lorsque le mot de passe change réellement, quelle que soit la raison pour laquelle il a changé.
Pour réellement résoudre votre problème, je ne considérerais pas les transactions distribuées, mais plutôt regarder dans le sens de la livraison de messages au moins une fois et du traitement idempotent.
Au moins une fois
Pour vous assurer que l'événement de changement de mot de passe est réellement vu par tous les consommateurs, vous devez utiliser un canal de communication durable où les messages peuvent être consommés dans un style "au moins une fois". Les consommateurs reconnaissent un message comme consommé uniquement lorsqu'ils l'ont entièrement traité. Si, par exemple, le service d'historique des mots de passe se bloque lors de l'écriture d'une entrée d'historique, il relira le même événement de changement de mot de passe après le redémarrage et réessayera, reconnaissant cet événement comme étant en lecture seule après avoir été lui-même écrit dans l'historique. Vous devez choisir une solution de file d'attente de messages en fonction de sa capacité à renvoyer des messages jusqu'à ce qu'ils soient reconnus.
Idempotence
Une fois la livraison effectuée au moins une fois, le problème est que des actions en double se produisent lorsqu'un message a été partiellement traité avant que le consommateur ne soit interrompu, puis retraité ultérieurement. Cela devrait être résolu en concevant chaque service afin qu'il soit idempotent. Soit les écritures qu'il effectue peuvent se produire plusieurs fois sans effets indésirables, soit il conserve sa propre mémoire des actions qu'il a effectuées et évite d'exécuter une action plus d'une fois. Dans le cas de l'envoi de courrier, vous constaterez que cela ne vaut probablement pas la peine d'essayer de le faire se comporter de manière idempotente et que tout va bien avec un courrier envoyé deux fois de temps en temps.
Dans tous les cas, faites attention à la façon dont vous effectuez vos services. Votre service d'historique de mot de passe doit-il vraiment être indépendant du service de changement de mot de passe?
la source
Je suis en désaccord avec beaucoup de réponses.
Il y a d'autres promesses de cohérence que vous pouvez ajouter.
Ces consistances supplémentaires devront être mises en œuvre en fonction des actes de la demande.
Je n'ai aucune idée de ce que vous entendez par «met à jour l'historique», mais veuillez ne jamais modifier l'historique. Si vous étendez simplement le DAG, cela devrait entraîner la modification de l'état actuel. Ils ne sont pas indépendants. S'ils le sont, vous ne pouvez pas vous fier à l'histoire qui reflète ce qui s'est passé. (et enfin et surtout, ne stockez pas les mots de passe voir comment ne pas stocker les mots de passe )
la source
consider asking the organization first.
. Vous avez probablement raison. Cependant, j'ai trouvé important de conditionner ces événements que nous ne pouvons pas annuler. Par exemple, des notifications à l'utilisateur final. Une notification reposant sur l'état réel des données de l'utilisateur peut entraîner une mauvaise impression.