Partitionslastenausgleich für die Ereignisverarbeitung

Der Partitionslastenausgleich ist eine Technik in Azure Event Hubs, die Ereignisverarbeitungsworkloads über mehrere Instanzen Ihrer Anwendung verteilt. Der Ereignisprozessorclient verwaltet automatisch das Partitionseigentum und koordiniert die Arbeitsverteilung zwischen allen aktiven Consumerinstanzen.

In den neueren SDK-Versionen (ab 5.0) übernimmt EventProcessorClient (.NET und Java) oder EventHubConsumerClient (Python und JavaScript) den Lastenausgleich automatisch. Sie abonnieren die Ereignisse, an denen Sie interessiert sind, indem Sie einen Ereignishandler registrieren.

In diesem Artikel wird ein Beispielszenario für die Verwendung mehrerer Instanzen von Clientanwendungen zum Lesen von Ereignissen aus einem Event Hub beschrieben. Außerdem werden wichtige Konzepte wie Partitionsbesitz, Checkpointing und Load-Balancing erläutert.

Tip

Wenn Sie eine ältere Version der Clientbibliothek verwenden, lesen Sie die Migrationshandbücher: .NET, Java, Python und JavaScript.

Hinweis

Der Schlüssel zur Skalierung in Event Hubs ist das Konzept der partitionierten Consumer. Im Gegensatz zum konkurrierenden Konsumentenmuster ermöglicht das partitionierte Konsumentenmuster eine hohe Skalierung, indem der Engpass durch Konkurrenz entfernt und die End-to-End-Parallelität erleichtert wird.

Beispielszenario

Als Beispielszenario soll ein Unternehmen für Alarmsysteme dienen, das 100.000 Häuser überwacht. Jede Minute gehen Daten von verschiedenen Sensoren im Unternehmen ein, z. B. von Bewegungsmeldern, Tür- und Fensteröffnungsmeldern oder Glasbruchsensoren, die in jedem Haus montiert sind. Das Unternehmen stellt für Kunden eine Website zum Überwachen der Aktivitäten im Haus nahezu in Echtzeit bereit.

Jeder Melder überträgt Daten an einen Event Hub. Der Event Hub ist mit 16 Partitionen konfiguriert. Auf der Verbraucherseite benötigen Sie einen Mechanismus, der diese Ereignisse lesen und konsolidieren (filtern, aggregieren usw.) und das Aggregat in einem Speicherblob speichern kann, welches dann auf einer benutzerfreundlichen Webseite angezeigt wird.

Verbraucheranwendung

Wenn Sie einen Consumer in einer verteilten Umgebung entwerfen, muss das Szenario die folgenden Anforderungen erfüllen:

  • Skalierung: Erstellen mehrerer Consumer, wobei jeder Consumer die Besitzrechte für das Lesen einiger Event Hubs-Partitionen übernimmt.
  • Lastenverteilung: Dynamisches Erhöhen oder Verringern der Verbraucher. Wenn beispielsweise in jedem Haus ein neuer Melder (z.B. ein Kohlenmonoxidmelder) montiert wird, erhöht sich die Anzahl der Ereignisse. In diesem Fall erhöht der (menschliche) Bediener die Anzahl der Consumerinstanzen. Anschließend kann der Pool von Konsumenten die Anzahl der Partitionen, die sie besitzen, neu ausbalancieren, um die Last mit den neu hinzugefügten Konsumenten zu teilen.
  • Nahtloses Fortsetzen nach Fehlern: Wenn bei einem Consumer (Consumer A) ein Fehler auftritt (der virtuelle Computer, der den Consumer hostet, stürzt z.B. plötzlich ab), müssen andere Consumer die Partitionen im Besitz von Consumer A aufnehmen und fortfahren können. Außerdem muss der Fortsetzungspunkt, der als Prüfpunkt oder Offset bezeichnet wird, genau an dem Punkt oder etwas früher liegen, an dem der Fehler bei Consumer A aufgetreten ist.
  • Nutzen von Ereignissen: Während die drei vorherigen Punkte die Verwaltung des Consumers betreffen, ist außerdem Code zum Nutzen und sinnvollen Verarbeiten der Ereignisse erforderlich. Beispielsweise zum Aggregieren und Hochladen in Blobspeicher.

Ereignisprozessor oder Consumer-Client

Sie müssen keine eigene Lösung erstellen, um diese Anforderungen zu erfüllen. Diese Funktionalität wird von den Azure-Event Hubs SDKs bereitgestellt. Verwenden Sie in .NET oder Java SDKs einen Ereignisprozessorclient (EventProcessorClient). Verwenden Sie in Python- und JavaScript-SDKs EventHubConsumerClient. In der alten Version des SDK unterstützte der Ereignisprozessorhost (EventProcessorHost) diese Features.

Verwenden Sie für die meisten Produktionsszenarien den Ereignisprozessorclient zum Lesen und Verarbeiten von Ereignissen. Der Prozessorclient bietet eine robuste Erfahrung für die Verarbeitung von Ereignissen in allen Partitionen eines Ereignishubs auf performante und fehlertolerante Weise und bietet gleichzeitig eine Möglichkeit, den Fortschritt zu überwachen. Ereignisprozessorclients können im Kontext einer Verbrauchergruppe für einen bestimmten Ereignishub zusammenarbeiten. Clients verwalten automatisch die Verteilung und den Ausgleich der Arbeit, wenn Instanzen für die Gruppe verfügbar oder nicht verfügbar sind.

Partitionszuständigkeit

Eine Ereignisprozessorinstanz ist in der Regel Besitzer von Ereignissen und verarbeitet Ereignisse aus mindestens einer Partition. Das System verteilt gleichmäßig die Besitzrechte an Partitionen zwischen allen aktiven Ereignisprozessorinstanzen, die einer Event Hub- und Consumergruppenkombination zugeordnet sind.

Jeder Ereignisprozessor verfügt über einen eindeutigen Bezeichner und beansprucht Besitz von Partitionen, indem ein Eintrag im Prüfpunktspeicher hinzugefügt oder aktualisiert wird. Alle Ereignisprozessorinstanzen kommunizieren in regelmäßigen Abständen mit diesem Speicher, um ihren eigenen Verarbeitungszustand zu aktualisieren und weitere aktive Instanzen zu erfahren. Das System verwendet diese Daten, um die Last zwischen den aktiven Prozessoren auszugleichen. Neue Instanzen können dem Verarbeitungspool zum Aufskalieren beitreten. Wenn Instanzen entweder aufgrund von Fehlern oder aufgrund von Herunterskalierung ausfallen, überträgt das System den Partitionsbesitz reibungslos an andere aktive Prozessoren.

Die Partitionsbesitz-Datensätze im Prüfpunktspeicher verfolgen die Namespaces der Event Hubs, den Event Hub-Namen, die Consumer-Gruppe, den Kennzeichner des Ereignisprozessors (auch Besitzer genannt), die Partitions-ID und den Zeitpunkt der letzten Änderung.

Event Hubs-Namespace Name des Event-Hubs Consumergruppe Besitzer Partitions-ID Zeitpunkt der letzten Änderung
mynamespace.servicebus.windows.net myeventhub meineVerbrauchergruppe 3be3f9d3-9d9e-4c50-9491-85ece8334ff6 0 2020-01-15T01:22:15
mynamespace.servicebus.windows.net myeventhub meineVerbrauchergruppe f5cc5176-ce96-4bb4-bbaa-a0e3a9054ecf 1 2020-01-15T01:22:17
mynamespace.servicebus.windows.net myeventhub meineVerbrauchergruppe 72b980e9-2efc-4ca7-ab1b-ffd7bece8472 2 2020-01-15T01:22:10
:
:
mynamespace.servicebus.windows.net myeventhub meineVerbrauchergruppe 844bd8fb-1f3a-4580-984d-6324f9e208af 15 2020-01-15T01:22:00

Jede Ereignisprozessorinstanz übernimmt den Besitz einer Partition und beginnt mit der Verarbeitung der Partition ab dem letzten bekannten Prüfpunkt. Wenn ein Prozessor fehlschlägt (VM wird heruntergefahren), erkennen andere Instanzen den Fehler, indem sie den Zeitpunkt der letzten Änderung betrachten. Andere Instanzen versuchen, den Besitz der Partitionen zu erlangen, die sich zuvor im Besitz der inaktiven Instanz befanden. Der Prüfpunktspeicher garantiert, dass nur eine der Instanzen erfolgreich den Besitz einer Partition beansprucht. Zu einem bestimmten Zeitpunkt gibt es also höchstens einen Prozessor, der Ereignisse von einer Partition empfängt.

Empfangen von Nachrichten

Wenn Sie einen Ereignisprozessor erstellen, geben Sie Funktionen an, die Ereignisse und Fehler verarbeiten. Jeder Aufruf der Funktion, die Ereignisse verarbeitet, liefert ein einzelnes Ereignis aus einer bestimmten Partition. Sie müssen dieses Ereignis behandeln. Wenn Sie sicherstellen möchten, dass der Consumer jede Nachricht mindestens einmal verarbeitet, schreiben Sie Ihren eigenen Code mit Wiederholungslogik. Seien Sie jedoch vorsichtig bei schädlichen Nachrichten.

Prozessereignisse relativ schnell bearbeiten. Das heißt: Führen Sie so wenig Verarbeitung wie möglich durch. Für das Schreiben in den Speicher und das Routing ist es besser, zwei Consumergruppen zu verwenden und zwei Ereignisprozessoren einzusetzen.

Prüfpunkt

Das Festlegen von Prüfpunkten ist ein Vorgang, durch den ein Ereignisprozessor die Position des letzten erfolgreich verarbeiteten Ereignisses innerhalb einer Partition markiert oder committet. Das Markieren eines Prüfpunkts erfolgt in der Regel innerhalb der Funktion, die die Ereignisse verarbeitet, und erfolgt pro Partition innerhalb einer Consumer-Gruppe.

Wenn ein Ereignisprozessor die Verbindung zu einer Partition verliert, kann eine andere Instanz die Verarbeitung ab dem Prüfpunkt fortsetzen, den der letzte Prozessor dieser Partition in der Verbrauchergruppe zuvor festgelegt hat. Wenn der Prozessor eine Verbindung herstellt, übergibt er den Offset an den Event Hub, um die Position für den Beginn des nächsten Lesevorgangs anzugeben. Auf diese Weise können mithilfe von Prüfpunkten Ereignisse von Downstreamanwendungen als abgeschlossen markiert werden. Darüber hinaus sorgen Prüfpunkte für Resilienz, wenn ein Ereignisprozessor ausfällt. Sie können zu älteren Daten zurückkehren, indem Sie einen niedrigeren Offset aus diesem Prüfpunktprozess angeben.

Wenn der Prüfpunkt ein Ereignis als verarbeitet markiert, wird ein Eintrag im Prüfpunktspeicher mit der Offset- und Sequenznummer des Ereignisses hinzugefügt oder aktualisiert. Legen Sie die Häufigkeit der Aktualisierung des Prüfpunkts fest. Das Aktualisieren nach jedem erfolgreich verarbeiteten Ereignis kann Auswirkungen auf die Leistung und die Kosten haben, da es einen Schreibvorgang in den zugrunde liegenden Prüfpunktspeicher auslöst. Außerdem deutet das Festlegen von Prüfpunkten für jedes einzelne Ereignis auf ein Warteschlangen-Messagingmuster hin, für das eine Service Bus-Warteschlange möglicherweise besser geeignet ist als ein Event Hub. Das Konzept hinter Event Hubs besteht darin, eine "mindestens einmalige Lieferung" in großem Umfang sicherzustellen. Indem Sie Ihre Downstreamsysteme idempotent machen, ist es einfach, nach Fehlern oder Neustarts, die dazu führen, dass dieselben Ereignisse mehrmals empfangen werden, eine Wiederherstellung durchzuführen.

Befolgen Sie diese Empfehlungen, wenn Sie Azure Blob Storage als Prüfpunktspeicher verwenden:

  • Verwenden Sie einen separaten Container für jede Consumergruppe. Sie können dasselbe Speicherkonto verwenden, aber verwenden Sie für jede Gruppe einen eigenen Container.
  • Verwenden Sie das Speicherkonto nicht für andere Elemente.
  • Verwenden Sie den Container nicht für andere Elemente.
  • Erstellen Sie das Speicherkonto in derselben Region wie die bereitgestellte Anwendung. Wenn die Anwendung lokal ist, versuchen Sie, die nächstgelegene Region auszuwählen.

Stellen Sie auf der Seite Speicherkonto im Azure-Portal im Abschnitt Blobdienst sicher, dass die folgenden Einstellungen deaktiviert sind.

  • Hierarchischer Namespace
  • Weiches Löschen von Blobs
  • Versionsverwaltung

Threadsicherheit und Prozessorinstanzen

Standardmäßig wird die Funktion zur Verarbeitung der Ereignisse für eine bestimmte Partition sequenziell aufgerufen. Nachfolgende Ereignisse und Aufrufe dieser Funktion aus derselben Partition werden im Hintergrund in der Warteschlange platziert, während die Ereignisschleife weiterhin im Hintergrund auf anderen Threads ausgeführt wird. Ereignisse aus unterschiedlichen Partitionen können gleichzeitig verarbeitet werden. Sie müssen jeden geteilten Zustand synchronisieren, auf den über Partitionen zugegriffen wird.

Sehen Sie sich die folgenden Schnellstarts an: