Eines der Hauptprobleme bei einem System mit Mikrodiensten ist die Funktionsweise von Transaktionen, die sich über verschiedene Dienste erstrecken. In unserer eigenen Architektur haben wir verteilte Transaktionen verwendet, um dies zu lösen, aber sie haben ihre eigenen Probleme. Besonders Deadlocks waren bisher ein Schmerz.
Eine andere Option scheint ein maßgeschneiderter Transaktionsmanager zu sein, der die Abläufe in Ihrem System kennt und die Rollbacks für Sie als Hintergrundprozess übernimmt, der sich über das gesamte System erstreckt und wenn sie nicht erreichbar sind, benachrichtigen Sie sie später).
Gibt es eine andere, akzeptierte Option? Beide scheinen ihre Nachteile zu haben. Der erste kann zu Deadlocks und vielen anderen Problemen führen, der zweite kann zu inkonsistenten Daten führen. Gibt es bessere Möglichkeiten?
quelle
Antworten:
Der übliche Ansatz besteht darin, diese Mikrodienste so weit wie möglich zu isolieren - sie als einzelne Einheiten zu behandeln. Dann können Transaktionen im Kontext des gesamten Dienstes entwickelt werden (dh nicht Teil der üblichen DB-Transaktionen, obwohl Sie immer noch DB-Transaktionen innerhalb des Dienstes haben können).
Überlegen Sie, wie Transaktionen ablaufen und welche Art von Transaktionen für Ihre Services sinnvoll sind. Dann können Sie einen Rollback-Mechanismus implementieren, der die ursprüngliche Operation nicht ausführt, oder ein 2-Phasen-Festschreibungssystem, das die ursprüngliche Operation reserviert, bis Sie aufgefordert werden, eine echte Festschreibung durchzuführen. Beide Systeme bedeuten natürlich, dass Sie Ihre eigenen implementieren, aber dann implementieren Sie bereits Ihre Microservices.
Finanzdienstleistungen erledigen dies die ganze Zeit. Wenn ich Geld von meiner Bank zu Ihrer Bank transferieren möchte, gibt es keine einzige Transaktion, die Sie in einer DB hätten. Sie wissen nicht, auf welchen Systemen eine der beiden Banken ausgeführt wird, und müssen sie daher wie Ihre Microservices behandeln. In diesem Fall würde meine Bank mein Geld von meinem Konto auf ein Guthabenkonto verschieben und dann Ihrer Bank mitteilen, dass sie etwas Geld hat. Wenn der Versand fehlschlägt, erstattet meine Bank mein Konto mit dem Geld, das sie zu senden versucht haben.
quelle
Ich denke, die Standardweisheit ist, dass Transaktionen niemals die Grenzen von Mikrodiensten überschreiten. Wenn ein gegebener Datensatz wirklich atomar mit einem anderen übereinstimmen muss, gehören diese beiden Dinge zusammen.
Dies ist einer der Gründe, warum es sehr schwierig ist, ein System in Dienste aufzuteilen, bis Sie es vollständig entworfen haben. Was in der modernen Welt wahrscheinlich bedeutet, dass es geschrieben wurde ...
quelle
Ich bin der Meinung, dass Sie sich fragen sollten, ob Microservices der bessere Ansatz ist, wenn Konsistenz eine wichtige Voraussetzung für Ihre Anwendung ist. Wie Martin Fowler sagt :
Aber vielleicht können Sie in Ihrem Fall bei der Verfügbarkeit auf Konsistenz verzichten
Ich frage mich aber auch, ob es eine Strategie für verteilte Transaktionen in Microservices gibt, aber vielleicht sind die Kosten zu hoch. Ich wollte Ihnen meine zwei Cent mit dem immer ausgezeichneten Artikel von Martin Fowler und dem CAP- Theorem geben.
quelle
Wie in mindestens einer der Antworten hier, aber auch an anderer Stelle im Web vorgeschlagen, ist es möglich, einen Mikrodienst zu entwerfen, der Entitäten innerhalb einer normalen Transaktion zusammenhält, wenn Sie Konsistenz zwischen den beiden Entitäten benötigen.
Gleichzeitig kann es jedoch vorkommen, dass die Entitäten nicht zum selben Microservice gehören, z. B. Verkaufsunterlagen und Bestellunterlagen (wenn Sie etwas bestellen, um den Verkauf zu erfüllen). In solchen Fällen ist möglicherweise eine Möglichkeit erforderlich, um die Konsistenz zwischen den beiden Mikrodiensten sicherzustellen.
Traditionell wurden verteilte Transaktionen verwendet, und meiner Erfahrung nach funktionieren sie gut, bis sie auf eine Größe skaliert werden, bei der das Sperren zum Problem wird. Sie können die Sperre lockern, damit wirklich nur die relevanten Ressourcen (z. B. der zu verkaufende Gegenstand) durch eine Statusänderung "gesperrt" werden. Hier wird es jedoch schwierig, da Sie das Gebiet betreten, in dem Sie alle Ressourcen bauen müssen Logik, dies selbst zu tun, anstatt zu sagen, dass eine Datenbank es für Sie handhabt.
Ich habe mit Unternehmen zusammengearbeitet, die es auf sich genommen haben, ein eigenes Transaktionsframework für die Behandlung dieses komplexen Problems zu entwickeln, aber ich empfehle es nicht, da es teuer ist und Zeit braucht, bis es ausgereift ist.
Es gibt Produkte, die an Ihr System angeschraubt werden können und für Konsistenz sorgen. Eine Geschäftsprozess-Engine ist ein gutes Beispiel, und in der Regel übernehmen sie die Konsistenz schließlich und mithilfe der Kompensation. Andere Produkte funktionieren ähnlich. In der Regel befindet sich in der Nähe der Clients eine Softwareschicht, die sich mit Konsistenz und Transaktionen befasst und (Mikro-) Services für die eigentliche Geschäftsabwicklung aufruft . Ein solches Produkt ist ein generischer JCA-Connector, der mit Java EE-Lösungen verwendet werden kann (aus Gründen der Transparenz: Ich bin der Autor). Unter http://blog.maxant.co.uk/pebble/2015/08/04/1438716480000.html finden Sie weitere Details und eine ausführlichere Diskussion der hier angesprochenen Probleme.
Eine andere Möglichkeit, Transaktionen und Konsistenz zu verwalten, besteht darin, einen Anruf an einen Mikrodienst in einen Anruf an eine Transaktion wie eine Nachrichtenwarteschlange umzuwandeln. Nehmen Sie das Beispiel mit den Verkaufs- / Auftragsdatensätzen von oben - Sie können den Verkaufsmikroservice einfach eine Nachricht an das Auftragssystem senden lassen, die in der gleichen Transaktion festgeschrieben wird, die den Verkauf in die Datenbank schreibt. Das Ergebnis ist eine asynchrone Lösung, die sich sehr gut skalieren lässt . Mithilfe von Technologien wie Web-Sockets können Sie sogar das Problem des Blockierens umgehen, das häufig mit der Vergrößerung asynchroner Lösungen zusammenhängt. Weitere Ideen zu Mustern wie diesem finden Sie in einem anderen meiner Artikel: http://blog.maxant.co.uk/pebble/2015/08/11/1439322480000.html .
Unabhängig davon, für welche Lösung Sie sich letztendlich entscheiden, ist es wichtig zu erkennen, dass nur ein kleiner Teil Ihres Systems Dinge schreibt, die konsistent sein müssen - der meiste Zugriff ist wahrscheinlich schreibgeschützt. Bauen Sie aus diesem Grund das Transaktionsmanagement nur in die relevanten Teile des Systems ein, damit es weiterhin gut skaliert werden kann.
quelle
In Microservices gibt es drei Möglichkeiten, die Konsistenz zwischen diff zu erreichen. Dienstleistungen:
Orchestrierung - Ein Prozess, der die Transaktion und das Rollback zwischen Diensten verwaltet.
Choreografie - Service-Pass-Nachrichten untereinander und erreichen schließlich einen konsistenten Zustand.
Hybrid - Mischen der beiden oben genannten.
Eine vollständige Beschreibung finden Sie unter folgendem Link: https://medium.com/capital-one-developers/microservices-when-to-react-vs-orchestrate-c6b18308a14c
quelle
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.
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.
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.
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.
quelle
Ich würde mit der Zerlegung des Problemraums beginnen - der Identifizierung Ihrer Dienstgrenzen . Bei ordnungsgemäßer Ausführung müssen Sie niemals dienstübergreifende Transaktionen durchführen.
Unterschiedliche Services haben ihre eigenen Daten, Verhaltensweisen, Motivationskräfte, Regierungs- und Geschäftsregeln usw. Ein guter Anfang ist die Auflistung der Fähigkeiten Ihres Unternehmens auf höchster Ebene. Zum Beispiel Marketing, Vertrieb, Buchhaltung, Support. Ein weiterer Ausgangspunkt ist die Organisationsstruktur. Beachten Sie jedoch, dass dies eine Einschränkung darstellt. Aus bestimmten Gründen (beispielsweise aus politischen Gründen) ist dies möglicherweise nicht das optimale Geschäftszerlegungsschema. Ein strengerer Ansatz ist die Analyse der Wertschöpfungskette . Denken Sie daran, dass Ihre Dienste auch Personen umfassen können, es handelt sich nicht ausschließlich um Software. Die Dienste sollten über Ereignisse miteinander kommunizieren .
Der nächste Schritt ist, diese Dienste zu schnitzen. Als Ergebnis erhalten Sie noch relativ unabhängige Aggregate . Sie stellen eine Einheit der Konsistenz dar. Mit anderen Worten, ihre Interna sollten konsistent und ACID sein. Aggregate kommunizieren auch über Ereignisse miteinander.
Wenn Sie der Meinung sind, dass Ihre Domain zuerst Konsistenz erfordert, überlegen Sie es sich noch einmal. Keines der großen und unternehmenskritischen Systeme ist darauf ausgelegt. Sie sind alle verteilt und schließlich konsistent. Überprüfen Sie Pat Hellands klassisches Papier .
Hier finden Sie einige praktische Tipps zum Erstellen eines verteilten Systems.
quelle