Behandeln von Änderungen in einer ereignisgesteuerten Microservice-Architektur

9

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?

CGeense
quelle
Welches Format haben Ihre Nachrichten oder können Sie diese entwerfen? Einige Nachrichtenformate erlauben optionale Attribute. Abhängig von der Implementierung des Readers können Sie optionale Attribute hinzufügen, ohne alle Reader aktualisieren zu müssen.
Thomas Owens
Es steht mir frei, ein Format für meine Nachrichten auszuwählen. Ich denke, JSON ist der beste Weg. Es ist wichtig, dass diese verschiedenen Dienste in verschiedenen Sprachen erstellt werden. Deshalb ist ein allgemeines Format wie XML oder JSON erforderlich.
CGeense

Antworten:

1

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.

candied_orange
quelle
Würde dies nicht den Verkehr zwischen Diensten dramatisch erhöhen? Anhand des Beispiels in der Frage, einem UserRegisteredEreignis, 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.
Thomas Owens
@ThomasOwens Das Senden von Daten mit dem Ereignis bedeutet, dass ich N Nachrichten sende, wenn ich N Beobachter habe. Das Senden des Ereignisses allein bedeutet, dass ich 3N Nachrichten sende, von denen nur 1 das Datenpaket enthält. Das lässt sich auch über ein Netzwerk problemlos skalieren. Der einzige wesentliche Nachteil ist, dass sich Ihre Verzögerung verdreifacht. Ohne zu sagen, dass Sie für eine bestimmte Situation keine optimalere Lösung finden können. Ich zeige, dass Ereignissysteme und Datenversionen nicht gekoppelt werden müssen.
candied_orange
1
Die ganze Idee dieses Eventbusses ist es, die verschiedenen Dienste zu entkoppeln. Durch die Verwendung einer Middleware können wir sicherstellen, dass diese Dienste sich nicht kennen und existieren und kommunizieren können, ohne die Existenz des anderen zu kennen. Wenn wir den Status aus diesen Ereignissen entfernen und Dienste direkt miteinander verbinden lassen, koppeln wir diese Dienste. Auf diese Weise können wir niemals einen einzelnen Dienst erneut
bereitstellen,
1
Der Punkt hier ist, dass Ereignissystem oder nicht Sie erweiterbare Daten benötigen. JSON oder XML machen das gut, wenn Sie die zuvor eingerichteten Namen oder Strukturen nicht ändern. Ich habe das gleiche mit Sammlungen gemacht. Das Ereignissystem sollte sich nicht um Geschlechter kümmern. Wenn es Daten sendet, sollte es sie einfach weitergeben, und etwas am anderen Ende kümmert sich entweder um die Geschlechter oder nicht.
candied_orange
1

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.

pnschofield
quelle
0

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.

JimmyJames
quelle
-1

@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.

Sander Weerdenburg
quelle
Mein Punkt ist, dass es alle Arten von Änderungen behandeln muss. Stellen Sie sich einen Dienst vor, der ein Ereignis sendet. Und dieses Ereignis enthält eine Eigenschaft, die veraltet ist und entfernt werden sollte. Selbst wenn diese anderen Dienste die Eigenschaft nicht nutzen, werden sie einfach beschädigt, weil sie dies erwarten. Also habe ich diesen Artikel auf martinfowler.com über verbraucherorientierte Verträge gelesen : martinfowler.com/articles/consumerDrivenContracts.html Bei Anwendung dieses Prinzips. Jeder Anbieter (Event) weiß, was von ihm erwartet wird. Mit dieser Information kann er bestätigen, ob er irgendwelche Verbraucher kaputt macht.
CGeense