Ich mache ein Forschungsprojekt, in dem ich die Optionen erforsche, um Änderungen in einer ereignisgesteuerten Microservice-Architektur zu handhaben.
Nehmen wir also an, wir haben eine Anwendung, in der wir vier verschiedene Dienste haben. Jeder dieser Dienste verfügt über eine eigene Datenbank zum Speichern lokaler Daten.
In diesem Setup kommunizieren die vier Dienste über einen Ereignisbus miteinander. Wenn also in einem Dienst etwas passiert, wird ein Ereignis veröffentlicht. Alle anderen Dienste, die an dieser Veranstaltung interessiert sind, werden sie auf ihre eigene Weise verarbeiten.
In diesem Fall müssen die verschiedenen Dienste in der Architektur "Verträge" über den Inhalt dieser Ereignisse (Attribute usw.) haben. Dienste haben also eine "lose gekoppelte Abhängigkeit" von diesen Ereignissen
Meine Frage ist: Wie können wir mit Änderungen in diesen Ereignissen umgehen?
Angenommen, Dienst A registriert neue Benutzer in der Anwendung. Es wird also ein "" UserRegistered "-Ereignis gesendet. Service B nimmt dieses Ereignis auf und verarbeitet es. Einige Entwickler im Team von Service C haben jedoch entschieden, dass sie auch das Geschlecht eines registrierten Benutzers benötigen. Daher wird das Ereignis geändert und das Attribut gender wird dem Ereignis "UserRegistered" hinzugefügt.
Wie können wir sicherstellen, dass Service B immer noch dasselbe Ereignis mit diesem zusätzlichen Attribut erfassen kann, ohne es erneut bereitzustellen?
Und gibt es andere Möglichkeiten, um dieses Problem anzugehen, als diese Ereignisse zu versionieren?
Antworten:
Bei Ereignissen geht es nicht darum, was sich geändert hat. Sie sind ungefähr, wenn sich etwas ändert.
Ich kann ein Ereignissystem erstellen, das vollständig von den geänderten Inhalten entkoppelt ist. Auf diese Weise lerne ich aus einem Ereignis nur, dass ein Objekt aktualisiert wurde. Wenn es mich überhaupt interessiert, dass das Objekt aktualisiert wurde, sage ich, was auch immer mit diesem Objekt zu sprechen weiß, um zu fragen, was sich geändert hat.
Das löst nicht das Problem, diese Änderungen zu kommunizieren. Es verhindert nur, dass es Teil des Ereignissystems wird.
Ein Beispiel für eine Möglichkeit, das Problem unterschiedlicher Datenversionen zu lösen, besteht darin, dass der Beobachter dem beobachteten Objekt eine Sammlung erstellt und übergibt. Das beobachtete Objekt füllt die Sammlung mit den neuesten Daten und wenn die Steuerung zurückkehrt, haben Sie (der Beobachter) das, was Sie brauchen. Wenn es etwas gibt, das Sie nicht interessiert, weil Sie noch nie davon gehört haben, ignorieren Sie es einfach.
Viele andere Möglichkeiten, diese Katze zu häuten, aber genau diese habe ich in diesem Fall zum Laufen gebracht.
quelle
UserRegistered
Ereignis, würde, wenn es ein Ereignis gäbe, das keine Informationen über den Benutzer enthält, 1 veröffentlichte Nachricht an den Bus und dann {Anzahl interessierter Dienste} Anforderungen an den Benutzerdienst oder veröffentlichte Nachrichten gesendet zum Bus. Dann würde es {Anzahl interessierter Dienste} Nachrichten in verschiedenen Größen geben. Obwohl ich denke, dass dies wahrscheinlich ein saubereres Design auf dem Papier ist, bricht die Leistung, wenn sie ein Problem darstellt, in jedem nicht trivialen System zusammen, insbesondere über ein Netzwerk.Frameworks wie NServiceBus behandeln dies mithilfe der Ereignisversionierung mit polymorphem Nachrichtenversand.
Beispielsweise kann Version 1 von Service A ein Ereignis als IUserRegistered_v1 veröffentlichen. Wenn Service A Version 1.1 ein zusätzliches Feld enthalten muss, deklariert es möglicherweise die Schnittstelle IUserRegistered_v1_1, die von IUserRegistered_v1 erben würde, und deklariert einige zusätzliche Felder.
Wenn Service A ein IUserRegistered_v1_1-Ereignis veröffentlicht, sendet NServiceBus die Nachricht an alle Endpunkte, die entweder IUserRegistered_v1 oder IUserRegistered_v1_1 verarbeiten.
quelle
Schrittweise Verbesserung
Eine einfache Änderung des Modells besteht darin, dass Hörer, wenn sie sich als Beobachter registrieren, eine Liste oder eine andere Struktur der Datenelemente enthalten, über die sie Bescheid wissen möchten. Dies kann funktionieren, wenn die vom Service zurückgegebenen Daten einfach sind. Wenn Sie jedoch über eine ausreichende Menge hierarchischer Daten verfügen, kann die Implementierung sehr kompliziert werden.
Felsfest
Wenn Sie wirklich eine robuste Methode für dieses Design wünschen, entwerfen Sie den Service so, dass er einen Verlauf der Änderungen speichert, die an den darin gespeicherten Daten vorgenommen wurden. Im Wesentlichen aktualisieren Sie niemals Datensätze in Ihrer Datenbank. Sie fügen neue Datensätze hinzu, wobei jeder die Änderung darstellt. Jeder dieser neuen Datensätze ist einer Ereignis-ID zugeordnet, die die Aktion identifiziert. Ein gerader Datensatz wird mit allen relevanten Informationen über die Änderung gespeichert (wer, was, wann usw.). Dies hat einige andere Vorteile, die außerhalb des Rahmens dieser Antwort liegen, aber in diesem Artikel über den CAP-Satz erörtert werden .
Wenn eine Änderung vorgenommen wird, erstellen Sie den Ereignisdatensatz und fügen alle neuen Daten zu Ihrer Datenbank hinzu. Anschließend veröffentlichen Sie ein Ereignis für die Listener, das (minimal) die Ereignis-ID enthält. Die Listener können dann die dieser ID zugeordneten Daten anfordern und die Version der damit verbundenen Daten abrufen. Jeder Zuhörer kann dann alles bekommen, was er braucht, ohne die Bedürfnisse anderer Hörer miteinander zu verbinden. Ich würde empfehlen, dass Sie der Ereignismeldung eine Teilmenge der am häufigsten verwendeten Datenfelder hinzufügen, damit Listener Ereignisse herausfiltern können, an denen sie nicht interessiert sind. Dies kann die Chattiness des Prozesses verringern und einige Listener müssen möglicherweise nie anrufen überhaupt zurück. Dies schützt Sie auch vor Zeitproblemen. Wenn Sie nur den Dienst zurückrufen und die Daten basierend auf dem Schlüssel erhalten, Möglicherweise wurden zwischen dem Abrufen des Ereignisses und dem Abrufen der Daten für das Ereignis weitere Änderungen vorgenommen. Dies ist möglicherweise nicht für alle Hörer von Bedeutung, kann jedoch zu großen Problemen führen, wenn Sie alle Änderungen kennen müssen. Die oben beschriebene inkrementelle Designverbesserung ist mit diesem Ansatz kompatibel, wenn Sie ihn wirklich auf 11 stellen möchten.
Einiges davon mag für das, was Sie tun müssen, übertrieben sein, aber meiner Erfahrung nach werden Sie oder jemand, der mit Ihren Daten arbeitet, es irgendwann wollen, wenn Sie nicht genau wissen, wie sich ein Datensatz im Laufe der Zeit ändert.
quelle
@CandiedOrange macht einen gültigen Punkt in einem Kommentar zu seiner eigenen Antwort in Bezug auf erweiterbare Datenformate wie XML.
Es sollte keine Rolle spielen, solange Sie Daten hinzufügen. Geben Sie jedoch sinnvolle Standardeinstellungen für ältere Ereignisse / nicht erforderliche Felder an.
Sie sollten nur die Dienste aktualisieren müssen, die sich - in diesem Fall - um das Geschlecht kümmern. Ein XML / JSON-Parser sollte in der Lage sein, zusätzliche Daten für die anderen Dienste zu ignorieren. Dies hängt natürlich von Ihrer Wahl des Parser- und Ereignisdatenformats ab.
Ich bin jedoch nicht mit Ereignissen einverstanden, bei denen die relevanten Daten nicht vorliegen. Für die Ereignisbeschaffung sollten Ereignisse definieren, was sich geändert hat. Beim Empfang eines Ereignisses sollten andere Dienste keine Daten aus der Quelle des Ereignisses abrufen müssen.
quelle