Transaktionen über REST-Microservices hinweg?

195

Nehmen wir an, wir haben einen Benutzer, Wallet REST-Microservices und ein API-Gateway, das die Dinge zusammenhält. Wenn sich Bob auf unserer Website registriert, muss unser API-Gateway einen Benutzer über den Benutzer-Microservice und eine Brieftasche über den Wallet-Microservice erstellen.

Hier sind einige Szenarien, in denen etwas schief gehen könnte:

  • Die Erstellung des Benutzers Bob schlägt fehl: Das ist in Ordnung. Wir senden nur eine Fehlermeldung an den Bob. Wir verwenden SQL-Transaktionen, sodass niemand Bob jemals im System gesehen hat. Alles ist gut :)

  • Benutzer Bob wird erstellt, aber bevor unsere Brieftasche erstellt werden kann, stürzt unser API-Gateway schwer ab. Wir haben jetzt einen Benutzer ohne Brieftasche (inkonsistente Daten).

  • Benutzer Bob wird erstellt und während wir die Brieftasche erstellen, wird die HTTP-Verbindung unterbrochen. Die Erstellung der Brieftasche war möglicherweise erfolgreich oder nicht.

Welche Lösungen stehen zur Verfügung, um diese Art von Dateninkonsistenz zu verhindern? Gibt es Muster, mit denen Transaktionen mehrere REST-Anforderungen umfassen können? Ich habe die Wikipedia-Seite über Two-Phase-Commit gelesen, die dieses Problem zu berühren scheint, bin mir aber nicht sicher, wie ich es in der Praxis anwenden soll. Dieses Atomic Distributed Transactions: Ein RESTful-Designpapier scheint ebenfalls interessant zu sein, obwohl ich es noch nicht gelesen habe.

Alternativ weiß ich, dass REST möglicherweise nicht für diesen Anwendungsfall geeignet ist. Wäre es vielleicht die richtige Art, mit dieser Situation umzugehen, REST vollständig zu löschen und ein anderes Kommunikationsprotokoll wie ein Nachrichtenwarteschlangensystem zu verwenden? Oder sollte ich die Konsistenz in meinem Anwendungscode erzwingen (z. B. durch einen Hintergrundjob, der Inkonsistenzen erkennt und behebt, oder durch ein Attribut "state" in meinem Benutzermodell mit Werten "create", "created" usw.)?

Olivier Lalonde
quelle
3
Interessanter Link: news.ycombinator.com/item?id=7995130
Olivier Lalonde
3
Wenn ein Benutzer ohne Brieftasche keinen Sinn ergibt, warum sollte er dann einen separaten Microservice dafür erstellen? Vielleicht stimmt etwas überhaupt nicht mit der Architektur? Warum brauchen Sie übrigens ein generisches API-Gateway? Gibt es einen bestimmten Grund dafür?
Vladislav Rastrusny
4
@VladislavRastrusny es war ein fiktives Beispiel, aber man könnte sich vorstellen, dass der Brieftaschenservice zum Beispiel von Stripe verwaltet wird.
Olivier Lalonde
Sie können einen Prozessmanager verwenden, um die Transaktion zu verfolgen (Prozessmanager-Muster), oder jeden Microservice wissen lassen, wie ein Rollback ausgelöst wird (Saga-Manager-Muster) oder eine Art Zwei-Phasen-Commit durchführen ( blog.aspiresys.com/software-product-engineering) / producteering /… )
Andrew Pate
@VladislavRastrusny "Wenn ein Benutzer ohne Brieftasche keinen Sinn ergibt, warum sollte er dann einen separaten Microservice dafür erstellen?" - Abgesehen von der Tatsache, dass ein Benutzer ohne Brieftasche nicht existieren kann, haben sie keinen gemeinsamen Code. Daher werden zwei Teams Benutzer- und Wallet-Microservices unabhängig voneinander entwickeln und bereitstellen. Ist es nicht der springende Punkt, Microservices überhaupt zu machen?
Nik

Antworten:

148

Was keinen Sinn ergibt:

  • verteilte Transaktionen mit REST-Services . REST-Services sind per Definition zustandslos, daher sollten sie nicht an einer Transaktionsgrenze teilnehmen, die sich über mehr als einen Service erstreckt. Das Anwendungsszenario für die Benutzerregistrierung ist sinnvoll, aber das Design mit REST-Microservices zum Erstellen von Benutzer- und Wallet-Daten ist nicht gut.

Was wird Ihnen Kopfschmerzen bereiten:

  • EJBs mit verteilten Transaktionen . Es ist eines dieser Dinge, die in der Theorie funktionieren, aber nicht in der Praxis. Im Moment versuche ich, eine verteilte Transaktion für Remote-EJBs in JBoss EAP 6.3-Instanzen zum Laufen zu bringen. Wir haben wochenlang mit dem RedHat-Support gesprochen und es hat noch nicht funktioniert.
  • Zwei-Phasen-Commit-Lösungen im Allgemeinen . Ich denke das 2PC Protokoll ist ein großartiger Algorithmus (vor vielen Jahren habe ich es in C mit RPC implementiert). Es erfordert umfassende Fehlerbehebungsmechanismen mit Wiederholungsversuchen, Status-Repository usw. Die gesamte Komplexität ist im Transaktionsframework verborgen (Beispiel: JBoss Arjuna). 2PC ist jedoch nicht ausfallsicher. Es gibt Situationen, in denen die Transaktion einfach nicht abgeschlossen werden kann. Anschließend müssen Sie Datenbankinkonsistenzen manuell identifizieren und beheben. Es kann einmal in einer Million Transaktionen passieren, wenn Sie Glück haben, aber es kann einmal in 100 Transaktionen passieren, abhängig von Ihrer Plattform und Ihrem Szenario.
  • Sagas (Ausgleichstransaktionen) . Es gibt den Implementierungsaufwand für das Erstellen der Kompensationsoperationen und den Koordinierungsmechanismus zum Aktivieren der Kompensation am Ende. Aber auch eine Entschädigung ist nicht ausfallsicher. Es kann immer noch zu Inkonsistenzen kommen (= Kopfschmerzen).

Was ist wahrscheinlich die beste Alternative:

  • Eventuelle Konsistenz . Weder ACID-ähnliche verteilte Transaktionen noch Ausgleichstransaktionen sind ausfallsicher, und beide können zu Inkonsistenzen führen. Eine eventuelle Konsistenz ist oft besser als eine "gelegentliche Inkonsistenz". Es gibt verschiedene Designlösungen, wie zum Beispiel:
    • Sie können mithilfe der asynchronen Kommunikation eine robustere Lösung erstellen. In Ihrem Szenario könnte das API-Gateway bei der Registrierung von Bob eine Nachricht an eine NewUser-Warteschlange senden und dem Benutzer sofort antworten: "Sie erhalten eine E-Mail, um die Kontoerstellung zu bestätigen." Ein Warteschlangen-Verbraucherservice kann die Nachricht verarbeiten, die Datenbankänderungen in einer einzigen Transaktion durchführen und die E-Mail an Bob senden, um die Kontoerstellung zu benachrichtigen.
    • Der Benutzer-Microservice erstellt den Benutzerdatensatz und einen Brieftaschendatensatz in derselben Datenbank . In diesem Fall ist der Wallet Store im User-Microservice eine Replik des Master-Wallet-Stores, die nur für den Wallet-Microservice sichtbar ist. Es gibt einen Datensynchronisationsmechanismus, der triggerbasiert ist oder regelmäßig aktiviert wird, um Datenänderungen (z. B. neue Brieftaschen) vom Replikat an den Master zu senden und umgekehrt.

Aber was ist, wenn Sie synchrone Antworten benötigen?

  • Umbau der Microservices . Wenn die Lösung mit der Warteschlange nicht funktioniert, weil der Dienstkonsument sofort eine Antwort benötigt, möchte ich die Benutzer- und Brieftaschenfunktionalität so umgestalten, dass sie im selben Dienst (oder zumindest in derselben VM) zusammengefasst wird, um verteilte Transaktionen zu vermeiden ). Ja, es ist einen Schritt weiter von Microservices entfernt und näher an einem Monolithen, aber es erspart Ihnen Kopfschmerzen.
Paulo Merson
quelle
4
Die eventuelle Konsistenz hat bei mir funktioniert. In diesem Fall sollte die Warteschlange "NewUser" hoch verfügbar und belastbar sein.
Ram Bavireddi
@RamBavireddi unterstützen Kafka oder RabbitMQ ausfallsichere Warteschlangen?
v.oddou
@ v.oddou Ja, das tun sie.
Ram Bavireddi
2
@PauloMerson Ich bin mir nicht sicher, wie Sie sich unterscheiden. Was ist, wenn in Ihrer Konsistenz die Erstellung der Brieftasche fehlschlägt?
Balsick
2
@balsick Eine der Herausforderungen bei eventuellen Konsistenzeinstellungen ist die erhöhte Komplexität des Designs. Konsistenzprüfungen und Korrekturereignisse sind häufig erforderlich. Das Design der Lösung variiert. In der Antwort schlage ich die Situation vor, in der der Wallet-Datensatz in der Datenbank erstellt wird, wenn eine über einen Nachrichtenbroker gesendete Nachricht verarbeitet wird. In diesem Fall könnten wir einen Dead Letter Channel einrichten. Wenn also die Verarbeitung dieser Nachricht einen Fehler generiert, können wir die Nachricht an eine Dead Letter-Warteschlange senden und das für "Wallet" verantwortliche Team benachrichtigen.
Paulo Merson
66

Dies ist eine klassische Frage, die mir kürzlich während eines Interviews gestellt wurde. Wie rufe ich mehrere Webdienste auf und behalte trotzdem eine Art Fehlerbehandlung mitten in der Aufgabe bei? Heutzutage vermeiden wir beim Hochleistungsrechnen Zweiphasen-Commits. Ich habe vor vielen Jahren eine Zeitung über das sogenannte "Starbuck-Modell" für Transaktionen gelesen: Denken Sie an den Prozess des Bestellens, Bezahlens, Zubereitens und Empfangens des bei Starbuck bestellten Kaffees ... Ich vereinfache die Dinge zu stark, aber ein Zwei-Phasen-Commit-Modell würde dies tun Schlagen Sie vor, dass der gesamte Prozess eine einzige Verpackungstransaktion für alle erforderlichen Schritte ist, bis Sie Ihren Kaffee erhalten. Bei diesem Modell würden jedoch alle Mitarbeiter warten und aufhören zu arbeiten, bis Sie Ihren Kaffee erhalten. Siehst du das bild

Stattdessen ist das "Starbuck-Modell" produktiver, indem es dem "Best-Effort" -Modell folgt und Fehler im Prozess kompensiert. Erstens sorgen sie dafür, dass Sie bezahlen! Dann gibt es Nachrichtenwarteschlangen mit Ihrer Bestellung, die an die Tasse angehängt sind. Wenn dabei etwas schief geht, wie Sie Ihren Kaffee nicht bekommen haben, es nicht das ist, was Sie bestellt haben usw., treten wir in den Vergütungsprozess ein und stellen sicher, dass Sie das bekommen, was Sie wollen oder Sie erstatten. Dies ist das effizienteste Modell für mehr Produktivität.

Manchmal verschwendet Starbucks einen Kaffee, aber der Gesamtprozess ist effizient. Es gibt andere Tricks, die Sie beim Erstellen Ihrer Webdienste berücksichtigen sollten, z. B. das Entwerfen so, dass sie beliebig oft aufgerufen werden können und dennoch das gleiche Endergebnis liefern. Meine Empfehlung lautet also:

  • Seien Sie bei der Definition Ihrer Webdienste nicht zu fein (ich bin nicht überzeugt von dem heutigen Hype um Mikrodienste: zu viele Risiken, zu weit zu gehen);

  • Async erhöht die Leistung. Ziehen Sie es daher vor, asynchron zu sein. Senden Sie Benachrichtigungen nach Möglichkeit per E-Mail.

  • Erstellen Sie intelligentere Services, um sie beliebig oft "abrufbar" zu machen, und verarbeiten Sie sie mit einer UID oder TaskID, die bis zum Ende der Reihenfolge von unten nach oben folgt, und validieren Sie die Geschäftsregeln in jedem Schritt.

  • Verwenden Sie Nachrichtenwarteschlangen (JMS oder andere) und leiten Sie zu Fehlerbehandlungsprozessoren um, die Operationen auf "Rollback" anwenden, indem Sie entgegengesetzte Operationen anwenden. Übrigens erfordert das Arbeiten mit asynchroner Reihenfolge eine Art Warteschlange, um den aktuellen Status des Prozesses zu überprüfen. also bedenke das;

  • Stellen Sie es als letztes Mittel (da es möglicherweise nicht häufig vorkommt) in eine Warteschlange, um Fehler manuell zu verarbeiten.

Kehren wir zum ursprünglichen Problem zurück, das veröffentlicht wurde. Erstellen Sie ein Konto und eine Brieftasche und stellen Sie sicher, dass alles erledigt wurde.

Angenommen, ein Webdienst wird aufgerufen, um den gesamten Vorgang zu orchestrieren.

Der Pseudocode des Webdienstes würde folgendermaßen aussehen:

  1. Rufen Sie den Microservice zur Kontoerstellung auf, übergeben Sie ihm einige Informationen und eine eindeutige Aufgaben-ID. 1.1 Der Microservice zur Kontoerstellung prüft zunächst, ob dieses Konto bereits erstellt wurde. Dem Kontoeintrag ist eine Aufgaben-ID zugeordnet. Der Microservice erkennt, dass das Konto nicht vorhanden ist, erstellt es und speichert die Task-ID. HINWEIS: Dieser Dienst kann 2000 Mal aufgerufen werden und führt immer das gleiche Ergebnis aus. Der Dienst antwortet mit einer "Quittung, die nur minimale Informationen enthält, um bei Bedarf einen Rückgängig-Vorgang auszuführen".

  2. Rufen Sie die Wallet-Erstellung auf und geben Sie die Konto-ID und die Aufgaben-ID an. Angenommen, eine Bedingung ist ungültig und die Brieftaschenerstellung kann nicht durchgeführt werden. Der Aufruf wird mit einem Fehler zurückgegeben, es wurde jedoch nichts erstellt.

  3. Der Orchestrator wird über den Fehler informiert. Es weiß, dass es die Kontoerstellung abbrechen muss, aber es wird es nicht selbst tun. Der Brieftaschendienst wird aufgefordert, dies zu tun, indem er die am Ende von Schritt 1 erhaltene "minimale Rückgängig-Quittung" weiterleitet.

  4. Der Kontodienst liest die Rückgängig-Quittung und weiß, wie der Vorgang rückgängig gemacht werden kann. Die Rückgängig-Quittung kann sogar Informationen über einen anderen Mikrodienst enthalten, den sie möglicherweise selbst aufgerufen hat, um einen Teil des Auftrags auszuführen. In diesem Fall kann der Rückgängig-Beleg die Konto-ID und möglicherweise einige zusätzliche Informationen enthalten, die für die Ausführung des umgekehrten Vorgangs erforderlich sind. In unserem Fall löschen wir das Konto zur Vereinfachung einfach mit seiner Konto-ID.

  5. Angenommen, der Webdienst hat (in diesem Fall) nie den Erfolg oder Misserfolg erhalten, dass das Rückgängigmachen der Kontoerstellung durchgeführt wurde. Der Rückgängig-Dienst des Kontos wird einfach erneut aufgerufen. Und dieser Dienst sollte normalerweise niemals fehlschlagen, da sein Ziel darin besteht, dass das Konto nicht mehr existiert. Es prüft also, ob es existiert und sieht, dass nichts getan werden kann, um es rückgängig zu machen. Es wird also zurückgegeben, dass die Operation ein Erfolg ist.

  6. Der Webdienst gibt an den Benutzer zurück, dass das Konto nicht erstellt werden konnte.

Dies ist ein synchrones Beispiel. Wir hätten es auf andere Weise verwalten und den Fall in eine Nachrichtenwarteschlange stellen können, die auf den Helpdesk ausgerichtet ist, wenn wir nicht möchten, dass das System den Fehler vollständig behebt. "Ich habe gesehen, dass dies in einem Unternehmen durchgeführt wird, in dem dies nicht ausreicht Das Back-End-System konnte mit Hooks versehen werden, um Situationen zu korrigieren. Der Helpdesk erhielt Nachrichten, die enthielten, was erfolgreich ausgeführt wurde, und verfügte über genügend Informationen, um Probleme zu beheben, für die unser Rückgängig-Beleg vollautomatisch verwendet werden konnte.

Ich habe eine Suche durchgeführt und die Microsoft-Website enthält eine Musterbeschreibung für diesen Ansatz. Es wird das kompensierende Transaktionsmuster genannt:

Ausgleich des Transaktionsmusters

user8098437
quelle
2
Denken Sie, Sie könnten diese Antwort erweitern, um dem OP spezifischere Ratschläge zu geben? Derzeit ist diese Antwort etwas vage und schwer zu verstehen. Obwohl ich verstehe, wie Kaffee bei Starbucks serviert wird, ist mir unklar, welche Aspekte dieses Systems in REST-Diensten emuliert werden sollten.
JWG
Ich habe ein Beispiel hinzugefügt, das sich auf den Fall bezieht, der ursprünglich im ursprünglichen Beitrag bereitgestellt wurde.
user8098437
2
Fügte gerade einen Link zum kompensierenden Transaktionsmuster hinzu, wie von Microsoft beschrieben.
user8098437
3
Für mich ist das die beste Antwort. So einfach
Oscar Nevarez
1
Beachten Sie, dass das Kompensieren von Transaktionen in bestimmten komplexen Szenarien möglicherweise völlig unmöglich ist (wie in den Microsoft-Dokumenten brillant hervorgehoben). Stellen Sie sich in diesem Beispiel vor, bevor die Brieftaschenerstellung fehlschlagen könnte, könnte jemand die Details des zugeordneten Kontos lesen, indem er einen GET-Aufruf für den Kontodienst ausführt, der idealerweise überhaupt nicht vorhanden sein sollte, da die Kontoerstellung fehlgeschlagen ist. Dies kann zu Dateninkonsistenzen führen. Dieses Isolationsproblem ist im SAGAS-Muster bekannt.
Anmol Singh Jaggi
32

Alle verteilten Systeme haben Probleme mit der Transaktionskonsistenz. Der beste Weg, dies zu tun, ist, wie Sie sagten, ein zweiphasiges Commit durchzuführen. Lassen Sie die Brieftasche und den Benutzer in einem ausstehenden Zustand erstellen. Rufen Sie nach dem Erstellen einen separaten Anruf an, um den Benutzer zu aktivieren.

Dieser letzte Anruf sollte sicher wiederholbar sein (falls Ihre Verbindung unterbrochen wird).

Dies erfordert, dass der letzte Aufruf beide Tabellen kennt (damit dies in einer einzelnen JDBC-Transaktion erfolgen kann).

Alternativ möchten Sie vielleicht darüber nachdenken, warum Sie sich so Sorgen um einen Benutzer ohne Brieftasche machen. Glauben Sie, dass dies ein Problem verursachen wird? Wenn ja, ist es vielleicht eine schlechte Idee, diese als separate Ruhegespräche zu haben. Wenn ein Benutzer ohne Brieftasche nicht existieren sollte, sollten Sie die Brieftasche wahrscheinlich dem Benutzer hinzufügen (im ursprünglichen POST-Aufruf, um den Benutzer zu erstellen).

Rob Conklin
quelle
Danke für den Vorschlag. Die User / Wallet-Dienste waren fiktiv, nur um den Punkt zu veranschaulichen. Ich stimme jedoch zu, dass ich das System so gestalten sollte, dass Transaktionen so weit wie möglich vermieden werden.
Olivier Lalonde
7
Ich stimme dem zweiten Standpunkt zu. Es scheint, dass Ihr Microservice, der Benutzer erstellt, auch eine Brieftasche erstellen sollte, da diese Operation eine atomare Arbeitseinheit darstellt. Sie können auch diese eaipatterns.com/docs/IEEE_Software_Design_2PC.pdf
Sattar Imamov
2
Das ist eigentlich eine tolle Idee. Undos sind Kopfschmerzen. Aber etwas in einem ausstehenden Zustand zu erstellen ist viel weniger invasiv. Es wurden alle Überprüfungen durchgeführt, es wurde jedoch noch keine endgültige Überprüfung erstellt. Jetzt müssen wir nur noch die erstellten Komponenten aktivieren. Wir können das wahrscheinlich sogar nicht transaktional tun.
Timo
10

Meiner Meinung nach ist einer der Schlüsselaspekte der Microservices-Architektur, dass die Transaktion auf den einzelnen Microservice beschränkt ist (Prinzip der Einzelverantwortung).

Im aktuellen Beispiel wäre die Benutzererstellung eine eigene Transaktion. Die Benutzererstellung würde ein USER_CREATED-Ereignis in eine Ereigniswarteschlange verschieben. Der Wallet-Dienst würde das USER_CREATED-Ereignis abonnieren und die Wallet-Erstellung durchführen.

Mithrandir
quelle
1
Angenommen, wir möchten alle 2PCs vermeiden und davon ausgehen, dass der Benutzerdienst in eine Datenbank schreibt, können wir die Nachricht nicht als Transaktion in eine Ereigniswarteschlange des Benutzers verschieben, was bedeutet, dass sie möglicherweise nie erreicht wird der Brieftaschenservice.
Roman Kharkovski
@ RomanKharkovski Ein wichtiger Punkt in der Tat. Eine Möglichkeit, dies zu beheben, besteht darin, eine Transaktion zu starten, den Benutzer zu speichern, das Ereignis (nicht Teil der Transaktion) zu veröffentlichen und dann die Transaktion festzuschreiben. (Im schlimmsten Fall, höchst unwahrscheinlich, schlägt das Festschreiben fehl und diejenigen, die auf das Ereignis reagieren, können den Benutzer nicht finden.)
Timo
1
Speichern Sie dann das Ereignis in der Datenbank sowie in der Entität. Haben Sie einen geplanten Job, um gespeicherte Ereignisse zu verarbeiten und an den Nachrichtenbroker zu senden. stackoverflow.com/a/52216427/4587961
Yan Khonski
7

Wenn meine Brieftasche nur eine weitere Gruppe von Datensätzen in derselben SQL-Datenbank wie der Benutzer wäre, würde ich wahrscheinlich den Benutzer- und Brieftaschenerstellungscode in denselben Dienst stellen und dies mit den normalen Datenbank-Transaktionsfunktionen verarbeiten.

Für mich klingt es so, als würden Sie fragen, was passiert, wenn der Code zur Erstellung der Brieftasche erfordert, dass Sie ein anderes System oder andere Systeme berühren? Ich würde sagen, es hängt alles davon ab, wie komplex und / oder riskant der Erstellungsprozess ist.

Wenn es nur darum geht, einen anderen zuverlässigen Datenspeicher zu berühren (z. B. einen, der nicht an Ihren SQL-Transaktionen teilnehmen kann), bin ich abhängig von den Gesamtsystemparametern möglicherweise bereit, die verschwindend geringe Wahrscheinlichkeit zu riskieren, dass kein zweiter Schreibvorgang stattfindet. Ich könnte nichts tun, aber eine Ausnahme auslösen und die inkonsistenten Daten über eine Ausgleichstransaktion oder sogar eine Ad-hoc-Methode behandeln. Wie ich meinen Entwicklern immer sage: "Wenn so etwas in der App passiert, bleibt es nicht unbemerkt."

Mit zunehmender Komplexität und zunehmendem Risiko bei der Erstellung von Brieftaschen müssen Sie Maßnahmen ergreifen, um die damit verbundenen Risiken zu verringern. Angenommen, einige der Schritte erfordern das Aufrufen mehrerer Partner-APIs.

An dieser Stelle können Sie eine Nachrichtenwarteschlange zusammen mit dem Begriff der teilweise erstellten Benutzer und / oder Brieftaschen einführen.

Eine einfache und effektive Strategie, um sicherzustellen, dass Ihre Entitäten schließlich ordnungsgemäß erstellt werden, besteht darin, die Jobs erneut zu versuchen, bis sie erfolgreich sind. Viel hängt jedoch von den Anwendungsfällen für Ihre Anwendung ab.

Ich würde auch lange und gründlich darüber nachdenken, warum ich einen fehleranfälligen Schritt in meinem Bereitstellungsprozess hatte.

Robert Moskal
quelle
4

Eine einfache Lösung besteht darin, einen Benutzer mithilfe des Benutzerdienstes zu erstellen und einen Messaging-Bus zu verwenden, in dem der Benutzerdienst seine Ereignisse ausgibt. Der Wallet-Dienst registriert sich auf dem Messaging-Bus, lauscht dem vom Benutzer erstellten Ereignis und erstellt eine Wallet für den Benutzer. Wenn der Benutzer in der Zwischenzeit die Brieftaschen-Benutzeroberfläche aufruft, um seine Brieftasche anzuzeigen, überprüfen Sie, ob der Benutzer gerade erstellt wurde, und zeigen Sie an, dass die Erstellung Ihrer Brieftasche im Gange ist. Überprüfen Sie dies in der Zwischenzeit

Techagrammer
quelle
3

Welche Lösungen stehen zur Verfügung, um diese Art von Dateninkonsistenz zu verhindern?

Traditionell werden verteilte Transaktionsmanager verwendet. Vor ein paar Jahren in der Java EE Welt könnten Sie diese Dienste als erstellt EJB s , die an verschiedenen Knoten und Ihre API - Gateway eingesetzt wurden würde Remote - Aufrufe auf diese EJBs gemacht haben. Der Anwendungsserver (bei korrekter Konfiguration) stellt mithilfe eines zweiphasigen Commits automatisch sicher, dass die Transaktion auf jedem Knoten entweder festgeschrieben oder zurückgesetzt wird, sodass die Konsistenz gewährleistet ist. Dies erfordert jedoch, dass alle Dienste auf demselben Anwendungsservertyp bereitgestellt werden (damit sie kompatibel sind) und in Wirklichkeit nur mit Diensten funktionieren, die von einem einzelnen Unternehmen bereitgestellt werden.

Gibt es Muster, mit denen Transaktionen mehrere REST-Anforderungen umfassen können?

Für SOAP (ok, nicht REST) ​​gibt es die WS-AT- Spezifikation, aber kein Dienst, den ich jemals integrieren musste, unterstützt dies. Für REST hat JBoss etwas in der Pipeline . Andernfalls besteht das "Muster" darin, entweder ein Produkt zu finden, das Sie in Ihre Architektur einbinden können, oder eine eigene Lösung zu erstellen (nicht empfohlen).

Ich habe ein solches Produkt für Java EE veröffentlicht: https://github.com/maxant/genericconnector

Gemäß dem Papier, auf das Sie verweisen, gibt es auch das Try-Cancel / Confirm-Muster und das zugehörige Produkt von Atomikos.

BPEL Engines übernehmen die Konsistenz zwischen remote bereitgestellten Diensten mithilfe der Kompensation.

Alternativ weiß ich, dass REST möglicherweise nicht für diesen Anwendungsfall geeignet ist. Wäre es vielleicht die richtige Art, mit dieser Situation umzugehen, REST vollständig zu löschen und ein anderes Kommunikationsprotokoll wie ein Nachrichtenwarteschlangensystem zu verwenden?

Es gibt viele Möglichkeiten, nicht-transaktionale Ressourcen in eine Transaktion zu "binden":

  • Wie Sie vorschlagen, können Sie eine Transaktionsnachrichtenwarteschlange verwenden, die jedoch asynchron ist. Wenn Sie also von der Antwort abhängen, wird sie unübersichtlich.
  • Sie können die Tatsache, dass Sie die Back-End-Dienste aufrufen müssen, in Ihre Datenbank schreiben und dann die Back-End-Dienste mithilfe eines Stapels aufrufen. Wieder asynchron, kann also chaotisch werden.
  • Sie können eine Geschäftsprozess-Engine als API-Gateway verwenden, um die Back-End-Microservices zu orchestrieren.
  • Sie können, wie eingangs erwähnt, Remote-EJB verwenden, da dies verteilte Transaktionen sofort unterstützt.

Oder sollte ich die Konsistenz in meinem Anwendungscode erzwingen (z. B. durch einen Hintergrundjob, der Inkonsistenzen erkennt und behebt, oder durch ein Attribut "state" in meinem Benutzermodell mit Werten "create", "created" usw.)?

Playing Devils Advocate: Warum so etwas bauen, wenn es Produkte gibt, die das für Sie tun (siehe oben) und es wahrscheinlich besser machen als Sie, weil sie sich bewährt haben?

Ameise Kutschera
quelle
2

Persönlich mag ich die Idee von Micro Services, Modulen, die durch die Anwendungsfälle definiert werden, aber wie Ihre Frage erwähnt, haben sie Anpassungsprobleme für die klassischen Unternehmen wie Banken, Versicherungen, Telekommunikation usw.

Verteilte Transaktionen sind, wie viele bereits erwähnt haben, keine gute Wahl. Die Leute setzen jetzt mehr auf letztendlich konsistente Systeme, aber ich bin nicht sicher, ob dies für Banken, Versicherungen usw. funktionieren wird.

Ich habe einen Blog über meine vorgeschlagene Lösung geschrieben, vielleicht kann dies Ihnen helfen ...

https://mehmetsalgar.wordpress.com/2016/11/05/micro-services-fan-out-transaction-problems-and-solutions-with-spring-bootjboss-and-netflix-eureka/

Posthumecaver
quelle
0

Eventuelle Konsistenz ist hier der Schlüssel.

  • Einer der Dienste wird ausgewählt, um der Hauptverantwortliche für die Veranstaltung zu werden.
  • Dieser Dienst behandelt das ursprüngliche Ereignis mit einem einzigen Commit.
  • Der primäre Handler übernimmt die Verantwortung für die asynchrone Kommunikation der sekundären Effekte mit anderen Diensten.
  • Der primäre Handler übernimmt die Orchestrierung anderer Dienstaufrufe.

Der Kommandant ist für die verteilte Transaktion verantwortlich und übernimmt die Kontrolle. Es kennt die auszuführende Anweisung und koordiniert deren Ausführung. In den meisten Szenarien gibt es nur zwei Anweisungen, es können jedoch mehrere Anweisungen verarbeitet werden.

Der Kommandant übernimmt die Verantwortung für die Gewährleistung der Ausführung aller Anweisungen, und das bedeutet, dass er in den Ruhestand geht. Wenn der Commander versucht, das Remote-Update durchzuführen, und keine Antwort erhält, erfolgt keine Wiederholung. Auf diese Weise kann das System so konfiguriert werden, dass es weniger fehleranfällig ist und sich selbst heilt.

Wenn wir erneut versuchen, haben wir Idempotenz. Idempotenz ist die Eigenschaft, etwas zweimal so tun zu können, dass die Endergebnisse dieselben sind, als ob es nur einmal getan worden wäre. Wir benötigen Idempotenz beim Remote-Dienst oder bei der Datenquelle, damit der Befehl, wenn er ihn mehr als einmal empfängt, nur einmal verarbeitet wird.

Eventuelle Konsistenz Dies löst die meisten Herausforderungen bei verteilten Transaktionen. Wir müssen hier jedoch einige Punkte berücksichtigen. Auf jede fehlgeschlagene Transaktion folgt ein erneuter Versuch. Die Anzahl der versuchten erneuten Versuche hängt vom Kontext ab.

Konsistenz ist letztendlich möglich, dh während das System während eines erneuten Versuchs nicht konsistent ist, z. B. wenn ein Kunde ein Buch bestellt, eine Zahlung geleistet und dann die Lagermenge aktualisiert hat. Wenn die Bestandsaktualisierungsvorgänge fehlschlagen und davon ausgegangen wird, dass der letzte Bestand verfügbar war, ist das Buch weiterhin verfügbar, bis der Wiederholungsvorgang für die Bestandsaktualisierung erfolgreich war. Nach erfolgreicher Wiederholung ist Ihr System konsistent.

Viyaan Jhiingade
quelle
-2

Warum nicht die API Management (APIM) -Plattform verwenden, die Scripting / Programmierung unterstützt? So können Sie im APIM einen zusammengesetzten Dienst erstellen, ohne die Mikrodienste zu stören. Ich habe mit APIGEE für diesen Zweck entworfen.

sra
quelle