Es gibt viele Lösungen, die mehr Kompromisse eingehen, als mir gefällt. Zugegeben, wenn Ihr Anwendungsfall komplex ist, z. B. der Geldtransport zwischen verschiedenen Banken, können angenehmere Alternativen unmöglich sein. Aber schauen wir uns an, was wir in dem üblichen Szenario tun können, in dem die Verwendung von Microservices unsere potenziellen Datenbanktransaktionen beeinträchtigt.
Option 1: Vermeiden Sie Transaktionen, wenn dies möglich ist
Offensichtlich und bereits erwähnt, aber ideal, wenn wir es schaffen. Gehörten die Komponenten tatsächlich zum selben Microservice? Oder können wir die Systeme so umgestalten, dass die Transaktion unnötig wird? Vielleicht ist das Akzeptieren von Nicht-Transaktionalität das günstigste Opfer.
Option 2: Verwenden Sie eine Warteschlange
Wenn es genug Gewissheit ist , dass der andere Dienst wird erfolgreich an , was wir wollen , es zu tun, können wir sie über irgendeine Form der Warteschlange aufrufen. Der Artikel in der Warteschlange wird erst später abgeholt. Wir können jedoch sicherstellen, dass der Artikel in der Warteschlange steht .
Angenommen, Sie möchten eine Entität einfügen und eine E-Mail als einzelne Transaktion senden. Anstatt den Mailserver anzurufen, stellen wir die E-Mail in eine Tabelle.
Begin transaction
Insert entity
Insert e-mail
Commit transaction
Ein klarer Nachteil ist, dass mehrere Microservices Zugriff auf dieselbe Tabelle benötigen.
Option 3: Führen Sie die externe Arbeit zuletzt kurz vor Abschluss der Transaktion aus
Dieser Ansatz beruht auf der Annahme, dass es sehr unwahrscheinlich ist, dass die Transaktion fehlschlägt.
Begin transaction
Insert entity
Insert another entity
Make external call
Commit transaction
Wenn die Abfragen fehlschlagen, ist der externe Anruf noch nicht erfolgt. Wenn der externe Anruf fehlschlägt, wird die Transaktion niemals festgeschrieben.
Dieser Ansatz hat die Einschränkungen, dass wir nur einen externen Anruf tätigen können und dass er zuletzt durchgeführt werden muss (dh, wir können sein Ergebnis nicht in unseren Abfragen verwenden).
Option 4: Erstellen Sie Dinge in einem ausstehenden Zustand
Wie hier veröffentlicht , können mehrere Microservices verschiedene Komponenten erstellen, die sich nicht transaktionell in einem ausstehenden Zustand befinden.
Eine Validierung wird durchgeführt, es wird jedoch nichts in einem endgültigen Zustand erstellt. Nachdem alles erfolgreich erstellt wurde, wird jede Komponente aktiviert. Normalerweise ist diese Operation so einfach und die Wahrscheinlichkeit, dass etwas schief geht, so gering, dass wir es vielleicht sogar vorziehen, die Aktivierung nicht-transaktional durchzuführen.
Der größte Nachteil ist wahrscheinlich, dass wir das Vorhandensein ausstehender Posten berücksichtigen müssen. Bei jeder Auswahlabfrage muss berücksichtigt werden, ob ausstehende Daten einbezogen werden sollen. Die meisten sollten es ignorieren. Und Updates sind eine andere Geschichte.
Option 5: Lassen Sie den Mikrodienst seine Abfrage freigeben
Keine der anderen Optionen erledigt das für Sie? Dann lasst uns unorthodox werden .
Je nach Unternehmen kann dies inakzeptabel sein. Es ist mir bewusst. Das ist unorthodox. Wenn es nicht akzeptabel ist, gehen Sie einen anderen Weg. Aber wenn dies zu Ihrer Situation passt, löst es das Problem einfach und kraftvoll. Dies könnte der akzeptabelste Kompromiss sein.
Es gibt eine Möglichkeit, Abfragen von mehreren Microservices in eine einfache, einzelne Datenbanktransaktion umzuwandeln.
Geben Sie die Abfrage zurück, anstatt sie auszuführen.
Begin transaction
Execute our own query
Make external call, receiving a query
Execute received query
Commit transaction
In Bezug auf das Netzwerk muss jeder Mikrodienst auf jede Datenbank zugreifen können. Beachten Sie dies auch bei der zukünftigen Skalierung.
Wenn sich die an der Transaktion beteiligten Datenbanken auf demselben Server befinden, handelt es sich um eine reguläre Transaktion. Wenn sie sich auf verschiedenen Servern befinden, handelt es sich um eine verteilte Transaktion. Der Code ist unabhängig davon der gleiche.
Wir erhalten die Abfrage, einschließlich ihres Verbindungstyps, ihrer Parameter und ihrer Verbindungszeichenfolge. Wir können es in eine ordentliche ausführbare Befehlsklasse einbinden, um den Ablauf lesbar zu halten: Der Microservice-Aufruf führt zu einem Befehl, den wir als Teil unserer Transaktion ausführen.
Die Verbindungszeichenfolge ist das, was der ursprüngliche Mikrodienst uns gibt. In jeder Hinsicht wird die Abfrage weiterhin als von diesem Mikrodienst ausgeführt betrachtet. Wir leiten es nur physisch durch den Client-Microservice. Macht das einen Unterschied? Nun, wir können es mit einer anderen Abfrage in dieselbe Transaktion einfügen.
Wenn der Kompromiss akzeptabel ist, bietet dieser Ansatz die unkomplizierte Transaktionalität einer Monolith-Anwendung in einer Microservice-Architektur.