Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Eine ereignisgesteuerte Architektur besteht aus Ereignisproduzenten , die einen Datenstrom von Ereignissen, Ereigniskonsumenten generieren, die auf diese Ereignisse lauschen, und Ereigniskanäle (häufig als Ereignisbroker oder Aufnahmedienste implementiert), die Ereignisse von Produzenten an Verbraucher übertragen.
Architektur
Ereignisse werden nahezu in Echtzeit übermittelt, sodass Verbraucher sofort auf Ereignisse reagieren können, sobald sie auftreten. Die Hersteller werden von den Verbrauchern entkoppelt, was bedeutet, dass ein Hersteller nicht weiß, welche Verbraucher zuhören. Verbraucher werden auch voneinander entkoppelt, und in einem Veröffentlichungsabonnent-Modell sieht jeder Verbraucher alle Ereignisse.
Dieser Prozess unterscheidet sich von einem Muster konkurrierender Verbraucher. Im Muster "Konkurrierende Verbraucher" rufen Verbraucher Nachrichten aus einer Warteschlange ab. Jede Nachricht wird nur einmal verarbeitet, vorausgesetzt, es gibt keine Fehler. In einigen Systemen, z. B. Azure IoT, müssen Ereignisse in großer Menge verarbeitet werden.
Eine ereignisgesteuerte Architektur kann ein Veröffentlichungsabonnentmodell oder ein Ereignisstreammodell verwenden.
Publish-Subscribe: Die Publish-Subscribe-Messaging-Infrastruktur verwaltet Abonnements. Wenn ein Ereignis veröffentlicht wird, sendet es das Ereignis an jeden Abonnenten. Nachdem das Ereignis empfangen wurde, wird es nicht in einem dauerhaften Protokoll gespeichert, sodass neue Abonnenten keine früheren Ereignisse sehen. Es wird empfohlen, Azure Event Grid für Veröffentlichungsabonnentszenarien zu verwenden.
Ereignisstreaming: Ereignisse werden in ein Protokoll geschrieben. Ereignisse werden in einer Partition streng sortiert und sind dauerhaft. Clients abonnieren den Stream nicht. Stattdessen kann ein Client aus einem beliebigen Teil des Datenstroms lesen. Der Client ist dafür verantwortlich, seine Position im Stream voranzutreiben, was bedeutet, dass der Client jederzeit teilnehmen und Ereignisse wiederholen kann. Diese Wiedergabefähigkeit unterstützt Wiederherstellungsszenarien, spät ankommende Verbraucher und die Erneute Verarbeitung nach einer Fehlerkorrektur. Azure Event Hubs ist für das Streaming mit hohem Durchsatz ausgelegt.
Auf der Verbraucherseite gibt es einige häufige Variationen:
Einfache Ereignisverarbeitung: Ein Ereignis löst sofort eine Aktion im Consumer aus. Sie können z. B. Azure Functions mit einem Event Grid-Trigger oder Azure Service Bus Trigger verwenden, damit ihr Code ausgeführt wird, wenn eine Nachricht veröffentlicht wird.
Grundlegende Ereigniskorrelation: Ein Verbraucher verarbeitet einige einzelne Geschäftsereignisse, korreliert sie anhand eines Bezeichners und speichert Informationen aus früheren Ereignissen, die beim Verarbeiten von späteren Ereignissen verwendet werden. Bibliotheken wie NServiceBus und MassTransit unterstützen dieses Muster.
Complex-Ereignisverarbeitung: Ein Verbraucher verwendet eine Technologie wie Azure Stream Analytics, um eine Reihe von Ereignissen zu analysieren und Muster in den Ereignisdaten zu identifizieren. Sie können beispielsweise Lesewerte von einem eingebetteten Gerät über ein Zeitfenster aggregieren und eine Benachrichtigung generieren, wenn der gleitende Durchschnitt einen bestimmten Schwellenwert überschreitet.
Event stream processing: Verwenden Sie eine Datenstreamingplattform, wie zum Beispiel Azure IoT Hub, Event Hubs oder Event Hubs für Apache Kafka, als Pipeline zum Aufnehmen von Ereignissen und um sie Stream-Prozessoren zuzuführen. Die Datenstromprozessoren verarbeiten oder transformieren den Datenstrom. Möglicherweise gibt es mehrere Datenstromprozessoren für verschiedene Subsysteme der Anwendung. Dieser Ansatz eignet sich gut für IoT-Workloads.
Die Quelle der Ereignisse kann außerhalb des Systems sein, z. B. physische Geräte in einer IoT-Lösung. In diesem Fall muss das System in der Lage sein, die Daten mit der Menge und dem Durchsatz aufzunehmen, die die Datenquelle benötigt.
Es gibt zwei primäre Ansätze zum Strukturieren von Ereignisnutzlasten. Wenn Sie die Kontrolle über Ihre Ereigniskonsumenten haben, können Sie die Nutzlaststruktur für jeden Verbraucher festlegen. Mit dieser Strategie können Sie Ansätze nach Bedarf innerhalb einer einzigen Workload kombinieren.
Alle erforderlichen Attribute in die Nutzlast einschließen: Verwenden Sie diesen Ansatz, wenn Verbraucher alle verfügbaren Informationen erhalten sollen, ohne eine externe Datenquelle abfragen zu müssen. Größere Nutzlasten erhöhen die Transportkosten und die Bandbreitennutzung und können aufgrund mehrerer Datensatzsysteme, insbesondere nach Updates, zu Datenkonsistenzproblemen führen. Vertragsverwaltung und Versionsverwaltung können auch komplex werden.
Nur Schlüssel in die Nutzlast einschließen: Bei diesem Ansatz rufen Verbraucher die erforderlichen Attribute, z. B. einen Primärschlüssel, ab, um die verbleibenden Daten aus einer Datenquelle unabhängig abzurufen. Diese Methode bietet eine bessere Datenkonsistenz, da sie über ein einzelnes Datensatzsystem verfügt. Es kann jedoch eine schlechtere Leistung haben als der erste Ansatz, da Verbraucher die Datenquelle häufig abfragen müssen. Sie haben weniger Bedenken hinsichtlich Kopplung, Bandbreite, Vertragsverwaltung oder Versionsverwaltung, da kleinere Ereignisse und einfachere Verträge die Komplexität verringern. Weitere Informationen finden Sie unter "Setzen Ihrer Ereignisse auf eine Diät".
Im vorherigen Diagramm wird jeder Verbrauchertyp als einzelnes Feld angezeigt. Um zu vermeiden, dass der Verbraucher zu einem einzigen Fehlerpunkt im System wird, ist es typisch, mehrere Instanzen eines Verbrauchers zu haben. Mehrere Instanzen können möglicherweise erforderlich sein, um das Volumen und die Häufigkeit von Ereignissen zu behandeln. Ein einzelner Consumer kann Ereignisse in mehreren Threads verarbeiten. Dieses Setup kann Herausforderungen schaffen, wenn Ereignisse in der richtigen Reihenfolge verarbeitet werden müssen oder genau-einmal-Semantik erfordern. Weitere Informationen finden Sie unter Minimieren der Koordination.
Es gibt zwei primäre Topologien in ereignisgesteuerten Architekturen:
Brokertopologie: Komponenten übertragen Ereignisse auf das gesamte System. Andere Komponenten reagieren entweder auf das Ereignis oder ignorieren das Ereignis. Diese Topologie ist nützlich, wenn der Ereignisverarbeitungsfluss relativ einfach ist. Es gibt keine zentrale Koordination oder Orchestrierung, sodass diese Topologie dynamisch sein kann.
Diese Topologie ist stark entkoppelt, wodurch Skalierbarkeit, Reaktionsfähigkeit und Fehlertoleranz von Komponenten gewährleistet werden. Keine Komponente besitzt oder erkennt den Status einer mehrstufigen Geschäftstransaktion, und Aktionen werden asynchron ausgeführt. Daher sind verteilte Transaktionen riskant, da es keinen integrierten Mechanismus zum Neustarten oder Wiedergeben gibt. Sie müssen fehlerbehandlungs- und manuelle Interventionsstrategien sorgfältig berücksichtigen, da diese Topologie eine Quelle für Dateninkonsistenzen sein kann.
Mediatortopologie: Diese Topologie behebt einige der Mängel der Brokertopologie. Es gibt einen Ereignisvermittler, der den Ablauf von Ereignissen verwaltet und steuert. Der Ereignisvermittler verwaltet den Zustand und verwaltet die Fehlerbehandlungs- und Neustartfunktionen. Im Gegensatz zur Brokertopologie sendet der Mediator Befehle an bestimmte Kanäle und nicht an das gesamte System. Diese Kanäle sind häufig Nachrichtenwarteschlangen. Es wird erwartet, dass Verbraucher diese Befehle verarbeiten.
Diese Topologie bietet mehr Kontrolle, bessere verteilte Fehlerbehandlung und potenziell bessere Datenkonsistenz. Diese Topologie führt jedoch zu einer erhöhten Kopplung zwischen Komponenten, und der Ereignisvermittler kann zu einem Engpass oder zu einem Zuverlässigkeitsproblem werden.
Wann diese Architektur verwendet werden soll
Sie sollten diese Architektur verwenden, wenn die folgenden Bedingungen erfüllt sind:
Mehrere Subsysteme müssen dieselben Ereignisse verarbeiten.
Die Echtzeitverarbeitung mit minimalem Zeitabstand ist erforderlich.
Komplexe Ereignisverarbeitung, z. B. Musterabgleich oder Aggregation im Zeitfenster, ist erforderlich.
Hohe Datenmenge und hohe Datengeschwindigkeit sind erforderlich, z. B. mit IoT.
Sie müssen Produzenten und Verbraucher für unabhängige Skalierbarkeits- und Zuverlässigkeitsziele entkoppeln.
Diese Architektur ist möglicherweise nicht geeignet, wenn:
Die Workload verfügt über einfache Anforderungsantwortworkflows, bei denen synchrone Aufrufe Ihre Latenz- und Durchsatzanforderungen erfüllen. Der operative Aufwand von Ereignisbrokern, der asynchronen Fehlerbehandlung und der eventuellen Konsistenz rechtfertigt sich nicht für unkomplizierte Interaktionen.
Geschäftstransaktionen erfordern eine starke Konsistenz in allen Diensten. Wenn Sie keine Zustände tolerieren können, in denen verschiedene Teile des Systems uneinig über den aktuellen Zustand sind, wirkt die letztendliche Konsistenz, die eine ereignisgesteuerte Architektur (EDA) mit sich bringt, gegen Sie.
Ihr Team hat keine Erfahrung mit verteilten asynchronen Systemen. Die Debugging-, Überwachungs- und Fehlerwiederherstellungsmuster, die EDA-Anforderungen erfordern, unterscheiden sich sinnvoll von denen in synchronen Architekturen, und die Lernkurve wirkt sich auf Übermittlungszeitachsen aus.
Vorteile
Diese Architektur bietet die folgenden Vorteile:
- Produzenten und Verbraucher werden entkoppelt.
- Es gibt keine Punkt-zu-Punkt-Integrationen. Neue Verbraucher können hinzugefügt werden, ohne produzenten oder andere Verbraucher zu ändern.
- Verbraucher können sofort auf Ereignisse reagieren, sobald sie auftreten.
- Es ist hochskalierbar, elastisch und verteilt.
- Subsysteme haben unabhängige Ansichten des Ereignisdatenstroms.
Herausforderungen
Garantierte Lieferung
In einigen Systemen, insbesondere in IoT-Szenarien, ist es von entscheidender Bedeutung, sicherzustellen, dass Ereignisse bereitgestellt werden.
Letztliche Konsistenz
Da Produzenten und Verbraucher über asynchrone Ereigniskanäle entkoppelt werden, werden Daten über Dienste hinweg nicht sofort konsistent, nachdem ein Ereignis veröffentlicht wurde. Verbraucher verarbeiten Ereignisse in ihrem eigenen Tempo, und es kann messbare Verzögerung zwischen dem Zeitpunkt geben, zu dem ein Hersteller eine Zustandsänderung ausgibt, und die Zeit, zu der alle Verbraucher diese Änderung widerspiegeln. Während dieses Fensters haben verschiedene Teile des Systems eine andere Ansicht des aktuellen Zustands.
Dieses Verhalten ist ein bewusster architektonischer Kompromiss. In vielen ereignisgesteuerten Designs entscheiden sich Architekten dafür, verfügbarkeits- und partitionstoleranz für bestimmte Workflows zu bevorzugen, die letztendliche Konsistenz als Kompromiss zu akzeptieren, während andere Workflows möglicherweise immer noch eine stärkere Konsistenz priorisieren. Architekten müssen Systeme entwerfen, bei denen Konsumenten und nachgeschaltete Lesevorgänge veraltete oder teilweise aktualisierte Daten tolerieren, wenn letztendliche Konsistenz in Kraft ist. Weitere Informationen finden Sie unter Minimieren der Koordination.
Verarbeiten von Ereignissen in reihenfolge oder nur einmal
Für Resilienz und Skalierbarkeit wird jeder Verbrauchertyp in der Regel in mehreren Instanzen ausgeführt. Wenn mehrere Instanzen ausgeführt werden, stellt dies eine Herausforderung dar, wenn die Ereignisse innerhalb eines Verbrauchertyps verarbeitet werden müssen oder wenn die idempotente Nachrichtenverarbeitungslogik nicht implementiert ist.
Nachrichtenkoordination über Dienste hinweg
Geschäftsprozesse verfügen häufig über mehrere Dienste, die Nachrichten veröffentlichen und abonnieren, um ein konsistentes Ergebnis über einen gesamten Workload hinweg zu erzielen. Sie können Workflowmuster wie Choreographie und Saga Orchestration verwenden, um Nachrichtenflüsse zuverlässig über verschiedene Dienste hinweg zu verwalten.
Fehlerbehandlung
Ereignisgesteuerte Architektur basiert in erster Linie auf asynchroner Kommunikation. Eine häufige Herausforderung, die eine asynchrone Kommunikation darstellt, ist die Fehlerbehandlung. Eine Möglichkeit, dieses Problem zu beheben, ist die Verwendung eines dedizierten Fehlerhandlerprozessors.
Wenn ein Ereignis consumer einen Fehler erkennt, sendet es sofort und asynchron das problematische Ereignis an den Fehlerhandlerprozessor und setzt die Verarbeitung anderer Ereignisse fort. Der Fehlerhandlerprozessor versucht, das Problem zu beheben. Wenn dies erfolgreich ist, sendet der Prozessor des Fehlerhandlers das Ereignis erneut an den ursprünglichen Aufnahmekanal. Wenn der Prozessor nicht erfolgreich ist, kann der Prozessor das Ereignis zur Administratorüberprüfung an eine Dead-Letter-Queue (DLQ) weiterleiten. Wenn Sie einen Fehlerbehandlungsprozessor verwenden, werden erneut übermittelte Ereignisse außerhalb der Reihenfolge verarbeitet.
Wenn ein Geschäftsprozess mehrere Dienste umfasst, sollten Sie eine Ausgleichstransaktion verwenden, um abgeschlossene Schritte logisch rückgängig zu machen, wenn ein späterer Schritt fehlschlägt.
Datenverlust
Eine weitere Herausforderung, die eine asynchrone Kommunikation darstellt, ist Datenverlust. Wenn eine der Komponenten abstürzt, bevor das Ereignis erfolgreich verarbeitet und an die nächste Komponente übergeben wird, wird das Ereignis verworfen und erreicht nie das endgültige Ziel. Um die Wahrscheinlichkeit eines Datenverlusts zu minimieren, speichern Sie Transitereignisse, und entfernen oder deaktivieren Sie die Ereignisse nur, wenn die nächste Komponente den Empfang des Ereignisses bestätigt. Diese Features werden als Client-Bestätigungsmodus und letzte Teilnehmerunterstützung bezeichnet.
Beobachtbarkeit über entkoppelte Komponenten hinweg
In synchronen Architekturen können Sie eine Anforderung über einen Aufrufstapel nachverfolgen. In ereignisgesteuerten Architekturen kann eine einzelne Geschäftstransaktion mehrere Produzenten, Kanäle und Consumer umfassen, die unabhängig und asynchron ausgeführt werden. Wenn etwas fehlschlägt oder sich unerwartet verhält, ist es schwieriger zu bestimmen, welche Komponente fehlerhaft funktioniert hat und warum, da kein gemeinsamer Aufrufkontext vorhanden ist.
Um die Sichtbarkeit aufrechtzuerhalten, fügen Sie eine Korrelationskennung in jedes Ereignis ein, damit alle nachgelagerten Verbraucher und Protokollierungssysteme verwandte Vorgänge mit einer einzigen Ablaufverfolgung verbinden können. Planen Sie die Instrumentierung von Anfang an ein, da die Nachrüstung der Überwachbarkeit in ein entkoppeltes System wesentlich schwieriger ist als die Implementierung.
Die gleiche Komplexität wirkt sich auf Tests aus. Das Überprüfen des End-to-End-Verhaltens über asynchrone, entkoppelte Komponenten erfordert bewusstere Teststrategien als synchrone Anrufketten.
Implementierung eines herkömmlichen Anforderungsantwortmusters
Manchmal erfordert der Ereignisproduzent eine sofortige Antwort des Ereignisanwenders, z. B. das Abrufen der Kundenberechtigung, bevor er mit einer Bestellung fortsetzt. In einer ereignisgesteuerten Architektur können Sie synchrone Kommunikation mithilfe von Anforderungsantwortnachrichten erreichen.
Dieses Muster wird mit einer Anforderungswarteschlange und einer Antwortwarteschlange implementiert. Der Ereignisproduzent sendet eine asynchrone Anforderung an eine Anforderungswarteschlange, hält andere Vorgänge für diese Aufgabe an und wartet auf eine Antwort in der Antwortwarteschlange. Dieser Ansatz wandelt dieses Muster effektiv in einen synchronen Prozess um. Ereigniskonsumenten verarbeiten dann die Anforderung und senden die Antwort zurück über eine Antwortwarteschlange. Bei diesem Ansatz wird in der Regel eine Sitzungs-ID für die Nachverfolgung verwendet, sodass der Ereignisproduzent weiß, welche Nachricht in der Antwortwarteschlange mit der spezifischen Anforderung verknüpft ist. Die ursprüngliche Anforderung kann auch den Namen der Antwortwarteschlange angeben, die möglicherweise kurzlebig ist, in einem Reply-to-Header oder in einem anderen benutzerdefinierten Attribut, das beidseitig vereinbart wurde.
Verwaltung der entsprechenden Anzahl von Ereignissen
Durch die Generierung einer übermäßigen Anzahl feinkörniger Ereignisse kann das System gesättigt und überwältigt werden. Ein übermäßiges Ereignisvolumen erschwert es, den Gesamtfluss von Ereignissen effektiv zu analysieren. Dieses Problem wird verschärft, wenn Änderungen zurückgesetzt werden müssen. Umgekehrt können übermäßig konsolidierende Ereignisse auch Probleme verursachen, was zu unnötigen Verarbeitungen und Antworten von Ereigniskonsumenten führt.
Um das richtige Gleichgewicht zu erzielen, berücksichtigen Sie die Folgen von Ereignissen und ob Nutzer die Payloads von Ereignissen überprüfen müssen, um ihre Reaktionen zu bestimmen. Wenn Sie beispielsweise über eine Kompatibilitätsprüfungskomponente verfügen, reicht es möglicherweise aus, nur zwei Arten von Ereignissen zu veröffentlichen: konform und nicht kompatibel. Dieser Ansatz trägt dazu bei, sicherzustellen, dass nur relevante Verbraucher jedes Ereignis verarbeiten, was unnötige Verarbeitung verhindert.
Ereignisschemaentwicklung
Produzenten und Verbraucher werden unabhängig bereitgestellt, sodass Sie nicht alle gleichzeitig aktualisieren können. Wenn ein Produzent die Struktur eines Ereignisses ändert, können Verbraucher, die das neue Schema noch nicht verstehen, unterbrechen. Definieren Sie frühzeitig eine Schema-Versionsstrategie und entwerfen Sie Konsumenten so, dass sie unbekannte Ereignisversionen verarbeiten können.
Weitere Überlegungen
Eine Anforderung ist nur für die Anforderungsbehandlungskomponente sichtbar. Ereignisse sind jedoch häufig für mehrere Komponenten in einer Workload sichtbar, auch wenn diese Komponenten sie nicht nutzen oder nicht dazu gedacht sind, sie zu nutzen. Achten Sie darauf, welche Informationen Sie in Ereignisse einbeziehen, um mit einer "Annahme von Sicherheitsverletzungen" zu arbeiten, um unbeabsichtigte Informationsexposition zu verhindern.
Viele Anwendungen verwenden ereignisgesteuerte Architektur als primäre Architektur. Sie können diesen Ansatz mit anderen Architekturstilen kombinieren, um eine Hybridarchitektur zu erstellen. Typische Kombinationen sind Microservices und Rohre und Filter. Integrieren Sie eine ereignisgesteuerte Architektur, um die Systemleistung zu verbessern, indem Sie Engpässe beseitigen und während hoher Anforderungsvolumen Gegendruck bereitstellen.
Bestimmte Domänen umfassen häufig mehrere Ereignishersteller, Verbraucher oder Ereigniskanäle. Änderungen an einer bestimmten Domäne können sich auf viele Komponenten auswirken.