
In Wir haben die Clusterbildung von RabbitMQ für Ausfallsicherheit und hohe Verfügbarkeit betrachtet. Jetzt tauchen wir tief in Apache Kafka ein.
Hier ist die Replizierungseinheit die Partition. Jeder Topic besteht aus einer oder mehreren Partitionen. Jede Partition hat einen Leader, entweder mit oder ohne Follower. Beim Erstellen eines Topics wird die Anzahl der Partitionen und der Replikationsfaktor angegeben. Ein gängiger Wert ist 3, was drei Replikate bedeutet: ein Leader und zwei Follower.

Abb. 1. Vier Partitionen sind auf drei Broker verteilt
Alle Lese- und Schreibanfragen gehen an den Leader. Follower senden periodisch Anfragen an den Leader, um die neuesten Nachrichten zu erhalten. Verbraucher wenden sich niemals an die Follower; letztere existieren nur für Redundanz und Ausfallsicherheit.

Partitionausfall
Wenn ein Broker ausfällt, brechen oft die Führungspunkte mehrerer Partitionen zusammen. In jedem dieser Fälle wird ein Follower von einem anderen Knoten zum neuen Leader. Das ist jedoch nicht immer der Fall, da auch der Synchronisierungsfaktor eine Rolle spielt: Gibt es synchronisierte Follower, und falls nicht, ist der Wechsel zu einer unsynchronisierten Replik erlaubt? Lassen Sie uns das vorerst nicht komplizierter machen.
Broker 3 fällt aus – und für Partition 2 wird ein neuer Leader auf Broker 2 gewählt.

Abb. 2. Broker 3 ist ausgefallen, und sein Follower auf Broker 2 wird zum neuen Leader von Partition 2 gewählt.
Dann fällt Broker 1 aus und Partition 1 verliert ebenfalls ihren Leader, dessen Rolle an Broker 2 übergeht.

Abb. 3. Es bleibt nur ein Broker übrig. Alle Leader befinden sich auf einem Broker ohne Redundanz.
Wenn Broker 1 wieder ins Netzwerk kommt, fügt er vier Follower hinzu, wodurch für jede Partition eine gewisse Redundanz geschaffen wird. Doch alle Leader bleiben weiterhin auf Broker 2.

Abb. 4. Die Leader bleiben auf Broker 2.
Wenn Broker 3 wieder hochgefahren wird, kehren wir zu drei Replikaten pro Partition zurück. Aber alle Leader sind immer noch auf Broker 2.

Abb. 5. Unausgewogene Platzierung der Leader nach der Wiederherstellung von Broker 1 und 3.
Kafka bietet ein besseres Werkzeug für das Rebalancing von Leadern als RabbitMQ. Dort musste man auf ein externes Plugin oder Skript zurückgreifen, das die Policies für die Migration des Hauptknotens änderte, um die Redundanz während der Migration zu reduzieren. Zudem musste man bei großen Warteschlangen mit einer Nichterreichbarkeit während der Synchronisation rechnen.
In Kafka gibt es das Konzept der 'bevorzugten Replikate' für die Rolle des Leaders. Bei der Erstellung von Topic-Partitionen versucht Kafka, die Leader gleichmäßig auf die Knoten zu verteilen und markiert diese ersten Leader als bevorzugt. Durch Serverneustarts, Ausfälle und Netzwerkunterbrechungen können die Leader jedoch im Laufe der Zeit auf andere Knoten verschoben werden, wie im oben beschriebenen Extremfall.
Um dies zu beheben, bietet Kafka zwei Optionen an:
- Option auto.leader.rebalance.enable=true ermöglicht es dem Controller-Knoten, die Leader automatisch auf die bevorzugten Replikate zurückzuweisen und so eine gleichmäßige Verteilung wiederherzustellen.
- Ein Administrator kann das Skript kafka-preferred-replica-election.sh manuell ausführen, um die Neuzuweisung vorzunehmen.

Abb. 6. Replikate nach dem Rebalancing
Dies war eine vereinfachte Darstellung des Fehlers, aber die Realität ist komplexer, auch wenn hier nichts allzu kompliziert ist. Es dreht sich alles um synchronisierte Replikate (In-Sync Replicas, ISR).
Synchronisierte Replikate (ISR)
ISR ist eine Gruppe von Replikaten eines Abschnitts, die als „synchronisiert“ (in-sync) gelten. Es gibt einen Leader, und Followers müssen nicht vorhanden sein. Ein Follower gilt als synchronisiert, wenn er exakte Kopien aller Nachrichten des Leaders innerhalb des Intervalls erstellt hat. replica.lag.time.max.ms.
Ein Follower wird aus der ISR-Gruppe entfernt, wenn er:
- keine Fetch-Anfrage innerhalb des Intervalls gestellt hat replica.lag.time.max.ms (gilt als tot)
- es versäumt hat, sich innerhalb des Intervalls zu aktualisieren replica.lag.time.max.ms (gilt als langsam)
Follower stellen Fetch-Anfragen innerhalb des Intervalls replica.fetch.wait.max.ms, das standardmäßig 500 ms beträgt.
Um den Zweck von ISR klar zu erklären, müssen wir uns die Bestätigungen vom Producer und einige Fehlerszenarien ansehen. Producer können wählen, wann der Broker die Bestätigung sendet:
- acks=0, keine Bestätigung wird gesendet
- acks=1, die Bestätigung wird gesendet, nachdem der Leader die Nachricht in sein lokales Protokoll geschrieben hat.
- acks=all, die Bestätigung wird gesendet, nachdem alle Replikate im ISR die Nachricht in ihren lokalen Protokollen gespeichert haben.
In der Terminologie von Kafka, wenn der ISR die Nachricht gespeichert hat, erfolgt deren 'Commit'. Acks=all ist die sicherste Option, bringt jedoch zusätzliche Verzögerungen mit sich. Betrachten wir zwei Ausfallbeispiele und wie die verschiedenen 'acks'-Optionen mit dem Konzept des ISR interagieren.
Acks=1 und ISR
In diesem Beispiel werden wir sehen, dass, wenn der Leader nicht darauf wartet, dass jede Nachricht von allen Followers gespeichert wird, bei einem Ausfall des Leaders Daten verloren gehen können. Der Übergang zu einem unsynchronisierten Follower kann durch die Einstellung "unclean.leader.election.enable" erlaubt oder verboten werden. unclean.leader.election.enable.
In diesem Beispiel hat der Producer den Wert acks=1 gesetzt. Die Partition ist auf alle drei Broker verteilt. Broker 3 hinkt hinterher; er hat sich vor acht Sekunden mit dem Leader synchronisiert und fehlt jetzt 7456 Nachrichten. Broker 1 hinkt nur eine Sekunde hinterher. Unser Producer sendet die Nachricht und erhält schnell eine Ack zurück, ohne Overhead aufgrund langsamer oder ausgefallener Followers, auf die der Leader nicht wartet.

Abb. 7. ISR mit drei Replikaten
Broker 2 fällt aus, und der Produzent erhält einen Verbindungsfehler. Nach dem Wechsel der Führungsrolle zu Broker 1 verlieren wir 123 Nachrichten. Der Follower auf Broker 1 war im ISR, konnte sich jedoch nicht vollständig mit dem Leader synchronisieren, als dieser ausfiel.

Abb. 8. Nachrichten gehen bei einem Ausfall verloren
In der Konfiguration bootstrap.servers sind mehrere Broker für den Produzenten aufgelistet, und er kann einen anderen Broker fragen, wer der neue Leader der Partition geworden ist. Dann stellt er eine Verbindung zu Broker 1 her und sendet weiterhin Nachrichten.

Abb. 9. Die Nachrichtenübertragung wird nach einer kurzen Unterbrechung fortgesetzt
Broker 3 ist noch weiter im Rückstand. Er stellt Abfragen zur Datenabfrage, kann sich jedoch nicht synchronisieren. Das kann an einer langsamen Netzwerkverbindung zwischen den Brokern, einem Speicherproblem usw. liegen. Er wird aus dem ISR entfernt. Jetzt besteht der ISR nur noch aus einer Replik — dem Leader! Der Produzent sendet weiterhin Nachrichten und erhält Bestätigungen.

Abb. 10. Der Follower auf Broker 3 wird aus dem ISR entfernt
Broker 1 fällt aus, und die Führungsrolle geht an Broker 3 mit dem Verlust von 15.286 Nachrichten! Der Produzent erhält eine Fehlermeldung über die Verbindung. Der Wechsel zum Führenden außerhalb des ISR war nur durch die Konfiguration möglich. unclean.leader.election.enable=true. Wenn es auf falsegesetzt ist, würde kein Wechsel stattfinden, und alle Lese- und Schreibanfragen würden abgelehnt. In diesem Fall warten wir auf die Rückkehr von Broker 1 mit seinen unberührten Daten in der Replik, die wieder die Führung übernehmen wird.

Abb. 11. Broker 1 fällt aus. Bei einem Ausfall gehen viele Nachrichten verloren.
Der Produzent stellt eine Verbindung zum letzten Broker her und sieht, dass dieser nun der Führer des Abschnitts ist. Er beginnt, Nachrichten an Broker 3 zu senden.

Abb. 12. Nach einer kurzen Unterbrechung werden die Nachrichten wieder an Abschnitt 0 gesendet.
Wir haben gesehen, dass der Produzent trotz kurzer Unterbrechungen zum Einrichten neuer Verbindungen und zum Finden eines neuen Führers ständig Nachrichten gesendet hat. Diese Konfiguration gewährleistet Verfügbarkeit auf Kosten der Konsistenz (Datensicherheit). Kafka hat Tausende von Nachrichten verloren, akzeptierte aber weiterhin neue Einträge.
Acks=all und ISR
Lass uns dieses Szenario noch einmal durchspielen, aber mit acks=all. Die Broker-Latenz beträgt im Durchschnitt vier Sekunden. Der Producer sendet eine Nachricht, acks=all, und erhält nun keine schnelle Antwort. Der Leader wartet, bis die Nachricht alle Replikate im ISR gespeichert hat.

Abb. 13. ISR mit drei Replikaten. Eine arbeitet langsam, was zu einer Verzögerung bei der Speicherung führt.
Nach vier Sekunden zusätzlicher Verzögerung sendet Broker 2 ein ACK. Alle Replikate sind jetzt vollständig aktualisiert.

Abb. 14. Alle Replikate speichern Nachrichten und es wird ein ACK gesendet.
Broker 3 hinkt nun noch weiter hinterher und wird aus dem ISR entfernt. Die Latenz wird erheblich reduziert, da im ISR keine langsamen Replikate mehr vorhanden sind. Broker 2 wartet nun nur noch auf Broker 1, der eine durchschnittliche Verzögerung von 500 ms hat.

Abb. 15. Die Replikation auf Broker 3 wird aus dem ISR entfernt.
Dann fällt Broker 2 aus, und die Führung geht ohne Verlust von Nachrichten an Broker 1 über.

Abb. 16. Broker 2 fällt aus.
Der Producer findet einen neuen Leader und beginnt, ihm Nachrichten zu senden. Die Latenz verringert sich weiter, da der ISR jetzt aus einem einzigen Replikat besteht! Daher trägt die Option acks=all keine Redundanz bei.

Abb. 17. Die Replikation auf Broker 1 übernimmt die Führung ohne Verlust von Nachrichten.
Dann fällt Broker 1 aus, und die Führung geht an Broker 3 mit dem Verlust von 14.238 Nachrichten!

Abb. 18. Broker 1 fällt aus, und der Wechsel der Führung mit der unclean-Konfiguration führt zu einem erheblichen Datenverlust
Wir könnten die Option nicht aktivieren unclean.leader.election.enable auf true. Standardmäßig ist sie auf false. Die Konfiguration acks=all mit unclean.leader.election.enable=true stellt eine Verfügbarkeit mit zusätzlichem Schutz der Daten sicher. Aber wie Sie sehen können, können wir dennoch Nachrichten verlieren.
Aber was, wenn wir die Datensicherheit erhöhen wollen? Wir können setzen unclean.leader.election.enable = false, aber das wird uns nicht unbedingt vor Datenverlust schützen. Wenn der Leader hart ausfällt und Daten verliert, sind die Nachrichten weiterhin verloren, zudem verliert man die Verfügbarkeit, bis der Administrator die Situation wiederherstellt.
Es ist besser, die Redundanz aller Nachrichten sicherzustellen, andernfalls sollte man auf das Schreiben verzichten. Dann ist aus Sicht des Brokers der Datenverlust nur bei zwei oder mehr gleichzeitigen Ausfällen möglich.
Acks=all, min.insync.replicas und ISR
Mit der Topic-Konfiguration min.insync.replicas erhöhen wir die Datensicherheit. Lassen Sie uns noch einmal den letzten Teil des vorherigen Szenarios durchgehen, aber diesmal mit min.insync.replicas=2.
Also hat Broker 2 einen Führer von Replikaten, während der Follower auf Broker 3 aus ISR entfernt ist.

Abb. 19. ISR aus zwei Replikaten
Broker 2 fällt aus, und die Führung wechselt ohne Nachrichtenverlust zu Broker 1. Aber nun besteht der ISR nur aus einem Replikat. Das entspricht nicht der minimalen Anzahl zur Erfassung von Aufzeichnungen, und daher antwortet der Broker auf den Schreibversuch mit einem Fehler. NotEnoughReplicas.

Abb. 20. Die Anzahl der ISR liegt um eins niedriger als in min.insync.replicas angegeben.
Diese Konfiguration opfert die Verfügbarkeit zugunsten der Konsistenz. Bevor eine Nachricht bestätigt wird, stellen wir sicher, dass sie auf mindestens zwei Replikate geschrieben wird. Das gibt dem Produzenten ein viel größeres Vertrauen. Hier ist ein Nachrichtenverlust nur möglich, wenn zwei Replikate gleichzeitig in einem kurzen Zeitraum ausfallen, bevor die Nachricht auf einen zusätzlichen Follower repliziert wird, was unwahrscheinlich ist. Wenn Sie jedoch extrem paranoid sind, können Sie einen Replikationsfaktor von 5 einstellen. min.insync.replicas Hier müssen sofort drei Broker gleichzeitig ausfallen, um einen Verlust der Aufzeichnung zu verursachen! Natürlich werden Sie für diese Zuverlässigkeit mit einer zusätzlichen Verzögerung bezahlen.
Wenn Verfügbarkeit zur Datensicherheit notwendig ist,
Wie im , ist manchmal Verfügbarkeit notwendig für die Datensicherheit. Sie sollten Folgendes bedenken:
- Kann ein Publisher einfach einen Fehler zurückgeben, während der übergeordnete Dienst oder Benutzer später einen neuen Versuch starten?
- Kann der Publisher die Nachricht lokal oder in einer Datenbank speichern, um es später erneut zu versuchen?
Wenn die Antwort negativ ist, erhöht die Optimierung der Verfügbarkeit die Datensicherheit. Sie verlieren weniger Daten, wenn Sie Verfügbarkeit anstelle einer Schreibverweigerung wählen. Letztendlich geht es darum, ein Gleichgewicht zu finden, und die Entscheidung hängt von der jeweiligen Situation ab.
Bedeutung von ISR
Das ISR-Set ermöglicht es, das optimale Gleichgewicht zwischen Datensicherheit und Latenz zu wählen. Beispielsweise kann die Verfügbarkeit während eines Ausfalls der meisten Replikate sichergestellt werden, während die Auswirkungen von toten oder langsamen Replikaten hinsichtlich der Latenz minimiert werden.
Wir bestimmen selbst den Wert replica.lag.time.max.ms entsprechend unseren Bedürfnissen. Im Wesentlichen bedeutet diese Einstellung, welche Latenz wir bereit sind hinzunehmen bei acks=all. Der Standardwert beträgt zehn Sekunden. Wenn dies für Sie zu lang ist, können Sie ihn verringern. In diesem Fall erhöht sich die Änderungsfrequenz im ISR, da Follower häufiger entfernt und hinzugefügt werden.
In RabbitMQ gibt es einfach eine Reihe von Spiegeln, die repliziert werden müssen. Langsame Spiegel führen zu zusätzlicher Verzögerung, und auf die Antwort von toten Spiegeln kann man bis zum Ablauf der Lebensdauer der Pakete warten, die die Verfügbarkeit jedes Knotens überprüfen (net tick). ISR ist eine interessante Methode, um diese Probleme mit erhöhten Latenzen zu vermeiden. Allerdings riskieren wir, Redundanz zu verlieren, da ISR nur auf den Leader reduziert werden kann. Um dieses Risiko zu umgehen, verwenden Sie die Einstellung. min.insync.replicas.
Kundenverbindungs-Garantie
In den Einstellungen bootstrap.servers können für Produzenten und Verbraucher mehrere Broker zur Verbindung der Clients angegeben werden. Die Idee ist, dass bei der Trennung eines Knotens mehrere Backups verfügbar bleiben, mit denen der Client eine Verbindung herstellen kann. Diese müssen nicht unbedingt die Leader der Partitionen sein, sondern dienen lediglich als Plattform für den anfänglichen Zugriff. Der Client kann sie fragen, auf welchem Knoten der Leader der Partition für Lese-/Schreiboperationen gehostet wird.
In RabbitMQ können Clients sich mit jedem Knoten verbinden, während die interne Routing-Logik die Anfragen an den richtigen Ort sendet. Das bedeutet, dass Sie einen Lastenausgleichsmechanismus vor RabbitMQ einrichten können. Kafka hingegen erfordert, dass sich Clients mit dem Knoten verbinden, auf dem der Leader des entsprechenden Bereichs gehostet wird. In solchen Situationen lässt sich kein Lastenausgleich einrichten. Die Liste bootstrap.servers ist von entscheidender Bedeutung, damit die Clients auf die richtigen Knoten zugreifen und sie nach einem Ausfall finden können.
Die Konsensarchitektur von Kafka
Bisher haben wir nicht besprochen, wie der Cluster von einem Ausfall eines Brokers erfährt und wie ein neuer Leader ausgewählt wird. Um zu verstehen, wie Kafka mit Netzwerkpartitionen umgeht, müssen wir zunächst die Konsensarchitektur verstehen.
Jeder Kafka-Cluster wird zusammen mit einem Zookeeper-Cluster bereitgestellt – einem Dienst für verteilten Konsens, der es dem System ermöglicht, einen Konsens über einen bestimmten Zustand zu erzielen, wobei die Konsistenz über die Verfügbarkeit priorisiert wird. Für die Genehmigung von Lese- und Schreiboperationen ist das Einverständnis der Mehrheit der Zookeeper-Knoten erforderlich.
Zookeeper speichert den Zustand des Clusters:
- Liste der Themen, Segmente, Konfiguration, aktuelle Replikate des Leaders, bevorzugte Replikate.
- Cluster-Mitglieder. Jeder Broker pingt den Zookeeper-Cluster. Wenn der Zookeeper innerhalb eines festgelegten Zeitraums kein Ping erhält, wird der Broker als nicht erreichbar vermerkt.
- Auswahl der Haupt- und Backup-Knoten für den Controller.
Der Controller-Knoten ist einer der Kafka-Broker, der für die Wahl der Leaders der Replikate verantwortlich ist. Der Zookeeper sendet dem Controller Benachrichtigungen über die Mitgliedschaft im Cluster und Änderungen an Themen, und der Controller muss entsprechend diesen Änderungen reagieren.
Nehmen wir zum Beispiel ein neues Thema mit zehn Segmenten und einem Replikationsfaktor von 3. Der Controller muss einen Leader für jedes Segment wählen und versucht dabei, die Leader optimal auf die Broker zu verteilen.
Für jedes Segment aktualisiert der Controller:
- Informationen im Zookeeper über ISR und Leader;
- sendet jedem Broker, der eine Replikation dieses Segments hostet, den LeaderAndISRCommand und informiert die Broker über ISR und Leader.
Wenn ein Broker mit dem Leader ausfällt, sendet Zookeeper eine Benachrichtigung an den Controller, der dann einen neuen Leader auswählt. Der Controller aktualisiert zuerst Zookeeper, bevor er jedem Broker einen Befehl sendet, um sie über den Führungswechsel zu informieren.
Jeder Leader ist für die Zusammenstellung der ISR verantwortlich. Die Konfiguration replica.lag.time.max.ms bestimmt, wer darin aufgenommen wird. Bei einer Änderung der ISR übergibt der Leader Zookeeper die neuen Informationen.
Zookeeper ist stets über alle Änderungen informiert, damit im Falle eines Ausfalls die Leitung reibungslos auf den neuen Leader übergeben werden kann.

Abb. 21. Konsens von Kafka
Replikationsprotokoll
Das Verständnis der Replikationsdetails hilft, potenzielle Datenausfall-Szenarien besser zu verstehen.
Abrufanfragen, Log End Offset (LEO) und Highwater Mark (HW)
Wir haben gesehen, dass Follower dem Leader periodisch Abrufanfragen (fetch) senden. Das Standardintervall beträgt 500 ms. Dies unterscheidet sich von RabbitMQ, bei dem die Replikation nicht durch das Spiegeln der Warteschlange, sondern durch den Master initiiert wird. Der Master sendet die Änderungen an die Spiegel.
Der Leader und alle Follower behalten den Log End Offset (LEO) und das Highwater-Merkmal (HW) bei. Der LEO speichert die Offset-Position der letzten Nachricht in der lokalen Replik, während HW das Offset der letzten Commit-Nachricht speichert. Beachten Sie, dass für den Status „Commit“ die Nachricht in allen Replikaten innerhalb des ISR (In-Sync Replicas) aufgezeichnet sein muss. Das bedeutet, dass LEO normalerweise etwas vor HW liegt.
Wenn der Leader eine Nachricht erhält, speichert er sie lokal. Der Follower sendet eine Abfrage mit seinem LEO. Der Leader sendet dann ein Paket von Nachrichten, beginnend mit diesem LEO und überträgt auch das aktuelle HW. Sobald der Leader die Information erhält, dass alle Replikate die Nachricht mit dem angegebenen Offset gespeichert haben, verschiebt er das HW-Merkmal. Nur der Leader kann das HW verschieben, und so erfahren alle Follower den aktuellen Wert in ihrenAntworten auf ihre Abfragen. Das bedeutet, dass die Follower sowohl bei den Nachrichten als auch beim Wissen über HW hinter dem Leader zurückbleiben können. Verbraucher erhalten Nachrichten nur bis zum aktuellen HW.
Bitte beachten Sie, dass „persistiert“ hier bedeutet, dass es im Speicher und nicht auf der Festplatte geschrieben wird. Zu Leistungszwecken synchronisiert Kafka die Daten mit einem bestimmten Intervall auf die Festplatte. Auch RabbitMQ hat ein solches Intervall, schickt jedoch die Bestätigung an den Publisher erst, nachdem sowohl der Master als auch alle Spiegel das Nachricht auf die Festplatte geschrieben haben. Aus Leistungsgründen haben die Entwickler von Kafka entschieden, die Bestätigung zu senden, sobald die Nachricht im Speicher abgelegt ist. Kafka setzt darauf, dass Redundanz das Risiko eines kurzfristigen Speicherns bestätigter Nachrichten nur im Speicher ausgleicht.
Leaderausfall
Wenn der Leader ausfällt, informiert Zookeeper den Controller, der dann eine neue Leader-Replik auswählt. Der neue Leader setzt einen neuen HW-Punkt gemäß seinem LEO fest. Dann erhalten die Follower die Information über den neuen Leader. Abhängig von der version von Kafka wählt der Follower eines von zwei Szenarien:
- Er wird das lokale Protokoll bis zur bekannten HW kürzen und den neuen Leader um Nachrichten nach diesem Punkt bitten.
- Sendet eine Anfrage an den neuen Leader, um die HW zum Zeitpunkt seiner Wahl zu erfahren, und kürzt dann das Log bis zu diesem Offset. Anschließend beginnt es, regelmäßige Abfragen zur Abtastung zu machen, beginnend mit diesem Offset.
Ein Follower muss sein Log aus folgenden Gründen kürzen:
- Wenn ein Leader ausfällt, gewinnt der erste Follower aus der ISR-Gruppe, der in Zookeeper registriert ist, die Wahl und wird zum Leader. Alle Follower in der ISR, obwohl als „synchronisiert“ betrachtet, haben möglicherweise nicht alle Nachrichtenkopien vom vorherigen Leader erhalten. Es ist durchaus möglich, dass der gewählte Follower nicht die aktuellste Kopie hat. Kafka garantiert, dass es keine Diskrepanzen zwischen den Replikaten gibt. Damit jede Diskrepanz vermieden wird, muss jeder Follower sein Log auf den HW-Wert des neuen Leaders zum Zeitpunkt seiner Wahl kürzen. Dies ist ein weiterer Grund, warum die Konfiguration acks=all für die Konsistenz so wichtig ist.
- Nachrichten werden regelmäßig auf die Festplatte geschrieben. Wenn alle Knoten des Clusters gleichzeitig ausfallen, werden die Replikate mit unterschiedlichen Offsets auf den Platten gespeichert. Es ist gut möglich, dass beim Wiedereintritt der Broker in das Netzwerk der neu gewählte Leader hinter seinen Followern zurückbleibt, weil er sich früher auf der Festplatte gespeichert hat als die anderen.
Wiedereingliederung in das Cluster
Bei der Wiedereingliederung in das Cluster verfahren die Replikate wie bei einem Ausfall des Leaders: Sie prüfen die Replik des Leaders und kürzen ihr Protokoll auf dessen HW (zum Zeitpunkt der Wahl). Im Vergleich dazu behandelt RabbitMQ wiederverbundene Knoten als völlig neu. In beiden Fällen verwirft der Broker jeden bestehenden Zustand. Bei Verwendung der automatischen Synchronisierung muss der Master sämtliche aktuellen Inhalte in das neue Spiegelbild replizieren, und der Master akzeptiert während dieser Operation keine Lese- oder Schreibvorgänge. Dieser Ansatz führt zu Problemen bei großen Warteschlangen.
Kafka ist ein verteiltes Logsystem, das insgesamt mehr Nachrichten speichert als eine RabbitMQ-Warteschlange, in der Daten nach dem Lesen aus der Warteschlange gelöscht werden. Aktive Warteschlangen sollten relativ klein bleiben. Kafka hingegen ist ein Logsystem mit einer eigenen Speicherpolitik, die Fristen in Tagen oder Wochen festlegen kann. Der Ansatz mit Warteschlangen-Sperrung und vollständiger Synchronisation ist für ein verteiltes Log völlig inakzeptabel. Stattdessen kürzen die Kafka-Follower ihr Log auf den HW-Leser (zum Zeitpunkt seiner Wahl), falls ihre Kopie dem Führer voraus ist. In dem wahrscheinlicheren Fall, dass der Follower hinterherhinkt, beginnt er einfach, Anfragen zur Abholung zu stellen, beginnend mit seinem aktuellen LEO.
Neue oder wiederverbundene Follower starten außerhalb des ISR und nehmen nicht an den Commits teil. Sie arbeiten einfach neben der Gruppe und empfangen Nachrichten so schnell sie können, bis sie den Führer eingeholt haben und in das ISR eintreten. Hier gibt es keine Sperrung, und es ist nicht notwendig, alle eigenen Daten zu verwerfen.
Verletzung der Konsistenz
Kafka verfügt über mehr Komponenten als RabbitMQ, daher gibt es hier ein komplexeres Verhalten, wenn die Konnektivität im Cluster beeinträchtigt wird. Kafka wurde jedoch ursprünglich für Cluster entworfen, sodass die Lösungen sehr gut durchdacht sind.
Im Folgenden sind einige Szenarien für Konnektivitätsstörungen aufgeführt:
- Szenario 1. Der Follower sieht den Leader nicht, kann aber noch Zookeeper sehen.
- Szenario 2. Der Leader sieht keinen Follower, kann aber noch Zookeeper sehen.
- Szenario 3. Der Follower sieht den Leader, sieht aber Zookeeper nicht.
- Szenario 4. Der Leader sieht die Follower, sieht aber Zookeeper nicht.
- Szenario 5. Der Follower ist vollständig von anderen Kafka-Knoten und von Zookeeper getrennt.
- Szenario 6. Der Leader ist vollständig von anderen Kafka-Knoten und von Zookeeper getrennt.
- Szenario 7. Der Kafka-Controller sieht keinen anderen Kafka-Knoten.
- Szenario 8. Der Kafka-Controller sieht Zookeeper nicht.
Für jedes Szenario gibt es spezifisches Verhalten.
Szenario 1. Der Follower sieht den Leader nicht, kann aber noch Zookeeper sehen.

Abb. 22. Szenario 1. ISR aus drei Replikaten.
Die Konnektivitätsstörung trennt Broker 3 von Broker 1 und 2, nicht jedoch von Zookeeper. Broker 3 kann keine Abfrageanfragen mehr senden. Nach Ablauf der Zeit. replica.lag.time.max.ms Er wird aus dem ISR entfernt und nimmt nicht an den Nachrichtencommits teil. Sobald die Konnektivität wiederhergestellt ist, wird er die Abfragen für die Auswahl fortsetzen und sich dem ISR anschließen, sobald er den Leader einholt. Zookeeper wird weiterhin Pings erhalten und annehmen, dass der Broker aktiv und gesund ist.

Abb. 23. Szenario 1. Der Broker wird aus dem ISR entfernt, wenn er innerhalb des Intervalls replica.lag.time.max.ms keine Auswahlanfrage erhält.
Es gibt keine logische Trennung (Split-Brain) oder eine Unterbrechung des Knotens, wie es bei RabbitMQ der Fall ist. Stattdessen wird die Redundanz verringert.
Szenario 2. Der Leader sieht keinen einzigen Follower, sieht aber immer noch Zookeeper.

Abb. 24. Szenario 2. Leader und zwei Follower.
Ein Verlust der Netzwerkverbindung trennt den Leader von den Followern, aber der Broker sieht immer noch Zookeeper. Wie im ersten Szenario wird der ISR komprimiert, aber diesmal nur bis zum Leader, da alle Follower aufhören, Auswahlanfragen zu senden. Auch hier gibt es keine logische Trennung. Stattdessen entsteht ein Verlust der Redundanz für neue Nachrichten, bis die Konnektivität wiederhergestellt ist. Zookeeper erhält weiterhin Pings und nimmt an, dass der Broker aktiv und gesund ist.

Abb. 25. Szenario 2. Der ISR hat sich nur bis zum Leader komprimiert.
Szenario 3. Der Follower sieht den Leader, aber nicht den Zookeeper
Der Follower trennt sich vom Zookeeper, bleibt jedoch mit dem Broker, der den Leader hat, verbunden. Infolgedessen führt der Follower weiterhin Abfrageanforderungen durch und bleibt Mitglied des ISR. Der Zookeeper erhält keine Pings mehr und registriert den Ausfall des Brokers, aber da es sich nur um einen Follower handelt, gibt es nach der Wiederherstellung keine Konsequenzen.

Abb. 26. Szenario 3. Der Follower sendet weiterhin Abfrageanforderungen an den Leader
Szenario 4. Der Leader sieht die Follower, aber nicht den Zookeeper

Abb. 27. Szenario 4. Leader und zwei Follower
Der Leader ist vom Zookeeper getrennt, aber nicht von den Brokern mit den Followern.

Abb. 28. Szenario 4. Der Leader ist vom Zookeeper isoliert
Nach einiger Zeit wird der Zookeeper den Ausfall des Brokers registrieren und den Controller darüber informieren. Dieser wird unter den Followern einen neuen Leader auswählen. Der ursprüngliche Leader wird jedoch weiterhin glauben, dass er der Leader ist, und weiterhin Einträge mit acks=1. Da die Follower ihm keine Abfrageanforderungen mehr senden, wird er sie als tot betrachten und versuchen, das ISR auf sich selbst zu reduzieren. Aber da er keine Verbindung zum Zookeeper hat, wird er dies nicht tun können und in diesem Moment aufhören, weitere Einträge zu empfangen.
Nachrichten acks=all werden nicht bestätigt, da der ISR zunächst alle Replikate einschließt und die Nachrichten sie nicht erreichen. Wenn der ursprüngliche Führer versucht, sie aus dem ISR zu entfernen, kann er dies nicht tun und hört ganz auf, irgendwelche Nachrichten zu empfangen.
Die Clients bemerken bald den Führungswechsel und fangen an, Nachrichten an den neuen Server zu senden. Sobald das Netzwerk wiederhergestellt ist, sieht der ursprüngliche Führer, dass er nicht mehr der Führer ist, und kürzt sein Log auf den HW-Wert, den der neue Führer zum Zeitpunkt des Ausfalls hatte, um Log-Divergenzen zu vermeiden. Dann beginnt er, Abfragen an den neuen Führer zu senden. Alle Nachrichten des ursprünglichen Führers, die nicht an den neuen Führer repliziert wurden, gehen verloren. Das bedeutet, dass Nachrichten, die in den paar Sekunden nicht vom ursprünglichen Führer bestätigt wurden, verloren gehen, als zwei Führer aktiv waren.

Abb. 29. Szenario 4. Der Führer auf Broker 1 wird nach der Wiederherstellung des Netzwerks zum Folger.
Szenario 5. Der Folger ist vollständig vom Rest der Kafka-Knoten und von Zookeeper getrennt.
Der Folger ist vollständig isoliert von den anderen Kafka-Knoten und von Zookeeper. Er wird einfach aus dem ISR entfernt, bis das Netzwerk wiederhergestellt ist, und holt dann die anderen nach.

Abb. 30. Szenario 5. Isolierter Follower wird aus dem ISR entfernt
Szenario 6. Der Leader ist vollständig von anderen Kafka-Knoten und Zookeeper isoliert

Abb. 31. Szenario 6. Leader und zwei Follower
Der Leader ist vollständig von seinen Followern, dem Controller und Zookeeper isoliert. Für kurze Zeit wird er weiterhin Aufzeichnungen entgegennehmen von acks=1.

Abb. 32. Szenario 6. Isolation des Leaders von anderen Kafka-Knoten und Zookeeper
Wenn er nach Ablauf von replica.lag.time.max.mskeine Anfragen erhält, wird er versuchen, den ISR auf sich selbst zu reduzieren, wird dies jedoch nicht tun können, da keine Verbindung zu Zookeeper besteht. Dann hört er auf, Aufzeichnungen entgegenzunehmen.
In der Zwischenzeit wird Zookeeper den isolierten Broker als tot markieren, und der Controller wird einen neuen Leader auswählen.

Abb. 33. Szenario 6. Zwei Leader
Der ursprüngliche Leader kann für einige Sekunden Aufzeichnungen entgegennehmen, hört dann jedoch auf, Nachrichten zu akzeptieren. Die Clients werden alle 60 Sekunden mit den neuesten Metadaten aktualisiert. Sie werden über den Wechsel des Leaders informiert und beginnen, Aufzeichnungen an den neuen Leader zu senden.

Abb. 34. Szenario 6. Produzenten wechseln zu dem neuen Leader
Alle bestätigten Einträge, die seit dem Verlust der Konnektivität vom ursprünglichen Leader gemacht wurden, gehen verloren. Sobald das Netzwerk wiederhergestellt ist, wird der ursprüngliche Leader über Zookeeper feststellen, dass er nicht mehr der Leader ist. Dann wird er sein Log auf den Stand des neuen Leaders zum Zeitpunkt seiner Wahl zurückschneiden und die Anfragen als Follower senden.

Abb. 35. Szenario 6. Der ursprüngliche Leader wird nach der Wiederherstellung der Netzwerkverbindung Follower.
In dieser Situation kann für einen kurzen Zeitraum eine logische Partition beobachtet werden, jedoch nur, wenn acks=1 und min.insync.replicas auch 1. Die logische Partition wird automatisch beendet, entweder nach der Wiederherstellung des Netzwerks, wenn der ursprüngliche Leader erkennt, dass er nicht mehr der Leader ist, oder wenn alle Clients verstehen, dass der Leader gewechselt hat und beginnen, an den neuen Leader zu schreiben – je nachdem, was zuerst eintritt. In jedem Fall wird es zu einem Verlust einiger Nachrichten kommen, jedoch nur mit acks=1.
Es gibt eine andere Variante dieses Szenarios, bei der die Follower kurz vor der Netzwerktrennung zurückbleiben und der Leader das ISR auf sich selbst komprimiert. Dann isoliert er sich aufgrund von Verbindungsverlust. Ein neuer Leader wird gewählt, aber der ursprüngliche Leader nimmt weiterhin Aufzeichnungen an, da es im ISR niemanden außer ihm gibt. Diese Aufzeichnungen gehen nach der Wiederherstellung des Netzwerks verloren. Der einzige Weg, um dieses Szenario zu vermeiden, ist acks=allmin.insync.replicas = 2 Szenario 7. Der Kafka-Controller sieht keinen anderen Kafka-Knoten.
Im Allgemeinen kann der Controller nach einem Verbindungsverlust mit einem Kafka-Knoten ihm keine Informationen über die Änderung des Leaders übermitteln. Im schlimmsten Fall führt dies zu einer kurzfristigen logischen Trennung, wie im Szenario 6. In der Regel wird der Broker einfach kein Kandidat für die Führung im Falle eines Ausfalls des letzten Knotens.
Szenario 8. Der Kafka-Controller sieht Zookeeper nicht
Szenario 8. Der Kafka-Controller kann Zookeeper nicht sehen
Von einem ausgefallenen Zookeeper-Controller erhält ein Kafka-Controller kein Ping mehr und wählt einen neuen Knoten aus. Der ursprüngliche Controller kann weiterhin so auftreten, erhält jedoch keine Benachrichtigungen von Zookeeper, weshalb er keine Aufgaben zu erledigen hat. Sobald das Netzwerk wiederhergestellt ist, erkennt er, dass er kein Controller mehr ist und nun ein normaler Kafka-Knoten geworden ist.
Ergebnisse aus den Szenarien
Wir sehen, dass der Verlust der Konnektivität von Followern nicht zu einem Verlust von Nachrichten führt, sondern vorübergehend die Redundanz verringert, bis das Netzwerk wiederhergestellt wird. Dies kann natürlich zu Datenverlust führen, wenn ein oder mehrere Knoten verloren gehen.
Wenn der Leader aufgrund von Verbindungsverlust vom Zookeeper getrennt wurde, kann dies zu einem Verlust von Nachrichten führen. acks=1Das Fehlen der Verbindung zum Zookeeper verursacht eine kurzfristige logische Trennung mit zwei Leaders. Dieses Problem wird durch die Einstellung acks=all.
Parameter min.insync.replicas in zwei oder mehr Replikaten gelöst, was zusätzliche Garantien bietet, dass solche kurzfristigen Szenarien nicht zu einem Verlust von Nachrichten führen, wie im Szenario 6.
Zusammenfassung zum Verlust von Nachrichten
Lassen Sie uns alle Möglichkeiten auflisten, wie Daten in Kafka verloren gehen können:
- Jeder Ausfall des Leaders, wenn Nachrichten durch acks=1
- Jeder unsaubere Wechsel der Führungsposition, d.h. zu einem Follower außerhalb des ISR, selbst unter acks=all
- Isolation des Leaders von Zookeeper, wenn Nachrichten durch acks=1
- Vollständige Isolation des Leaders, der die ISR-Gruppe bereits auf sich selbst reduziert hat. Alle Nachrichten gehen verloren, selbst acks=all. Dies gilt nur, wenn min.insync.replicas=1.
- Gleichzeitige Ausfälle aller Knoten eines Topics. Da Nachrichten im Speicher bestätigt werden, können einige möglicherweise noch nicht auf die Festplatte geschrieben worden sein. Nach dem Neustart der Server könnten einige Nachrichten fehlen.
Unsaubere Führungswechsel können entweder durch deren Verbot oder durch Sicherstellung von Redundanz von mindestens zwei vermieden werden. Die robusteste Konfiguration ist eine Kombination aus acks=all und min.insync.replicas mehr als 1.
Direkter Vergleich der Zuverlässigkeit von RabbitMQ und Kafka
Um Zuverlässigkeit und hohe Verfügbarkeit zu gewährleisten, implementieren beide Plattformen ein System der primären und sekundären Replikation. Allerdings hat RabbitMQ eine Achillesferse. Bei der Wiederherstellung nach einem Ausfall verwerfen die Knoten ihre Daten, und die Synchronisierung wird blockiert. Diese Doppelbelastung wirft Fragen zur Langlebigkeit großer Warteschlangen in RabbitMQ auf. Sie müssen entweder mit einer Verringerung der Redundanz oder mit langen Blockierungen leben. Eine Verringerung der Redundanz erhöht das Risiko eines massiven Datenverlusts. Wenn die Warteschlangen jedoch klein sind, kann die Gewährleistung von Redundanz mit kurzen Downtime-Phasen (einige Sekunden) durch Wiederholungsversuche gemeistert werden.
In Kafka gibt es dieses Problem nicht. Sie verwirft Daten nur an dem Punkt, an dem der Leader und der Follower nicht übereinstimmen. Alle gemeinsamen Daten bleiben erhalten. Darüber hinaus blockiert die Replikation das System nicht. Der Leader nimmt weiterhin Eingaben entgegen, während ein neuer Follower ihn einholt, sodass für DevOps das Hinzufügen oder Wiederherstellen des Clusters zu einer trivialen Aufgabe wird. Natürlich gibt es weiterhin Herausforderungen, wie etwa die Netzwerkbandbreite bei der Replikation. Wenn mehrere Follower gleichzeitig hinzugefügt werden, könnte man auf Bandbreitenbeschränkungen stoßen.
RabbitMQ übertrifft Kafka in Bezug auf die Zuverlässigkeit beim gleichzeitigen Ausfall mehrerer Server im Cluster. Wie bereits erwähnt, sendet RabbitMQ eine Bestätigung an den Publisher erst nach dem Speichern der Nachricht auf der Festplatte des Masters und aller Spiegel. Aber das fügt eine zusätzliche Verzögerung aus zwei Gründen hinzu:
- fsync alle paar Hundert Millisekunden
- Das Versagen eines Spiegels wird erst nach Ablauf der Lebensdauer der Pakete bemerkt, die die Verfügbarkeit jedes Knotens überprüfen (net tick). Wenn ein Spiegel verzögert oder ausgefallen ist, führt das zu Verzögerungen.
Kafka setzt darauf, dass wenn eine Nachricht auf mehreren Knoten gespeichert wird, Nachrichten bestätigt werden können, sobald sie in den Speicher gelangen. Dadurch entsteht das Risiko, Nachrichten jeglicher Art zu verlieren (auch acks=all, min.insync.Replikate=2) bei gleichzeitigen Ausfällen.
Insgesamt zeigt Kafka eine höhere Leistung und ist ursprünglich für Cluster konzipiert. Die Anzahl der Follower kann auf bis zu 11 erhöht werden, wenn dies für die Zuverlässigkeit erforderlich ist. Ein Replikationsfaktor von 5 und die minimale Anzahl der Replikate im synchronisierten Zustand min.insync.replicas=3 machen den Verlust von Nachrichten zu einem sehr seltenen Ereignis. Wenn Ihre Infrastruktur einen solchen Replikationsfaktor und ein entsprechendes Maß an Redundanz gewährleisten kann, können Sie diese Option wählen.
Die Clusterbildung von RabbitMQ eignet sich gut für kleine Warteschlangen. Aber selbst kleine Warteschlangen können bei hohem Traffic schnell anwachsen. Sobald die Warteschlangen groß werden, müssen Sie eine harte Entscheidung zwischen Verfügbarkeit und Zuverlässigkeit treffen. Die Clusterbildung von RabbitMQ ist am besten für untypische Situationen geeignet, in denen die Vorteile der Flexibilität von RabbitMQ die Nachteile seiner Clusterbildung überwiegen.
Eine der Lösungen für die Schwachstelle von RabbitMQ in Bezug auf große Warteschlangen besteht darin, diese in mehrere kleinere zu unterteilen. Wenn die vollständige Ordnung der gesamten Warteschlange nicht erforderlich ist, sondern nur die der relevanten Nachrichten (z. B. Nachrichten eines bestimmten Kunden), oder überhaupt keine Ordnung erforderlich ist, ist diese Option akzeptabel: Sehen Sie sich mein Projekt an. zum Aufteilen der Warteschlange (das Projekt befindet sich noch in der frühen Phase).
Vergessen Sie schließlich nicht die Reihe von Bugs in den Cluster- und Replikationsmechanismen sowohl bei RabbitMQ als auch bei Kafka. Mit der Zeit sind die Systeme reifer und stabiler geworden, aber keine Nachricht wird jemals zu 100 % vor dem Verlust geschützt sein! Außerdem gibt es in Rechenzentren großflächige Ausfälle!
Wenn ich etwas übersehen, einen Fehler gemacht habe oder Sie mit einer der Aussagen nicht einverstanden sind, zögern Sie nicht, einen Kommentar zu hinterlassen oder mich zu kontaktieren.
Oft werde ich gefragt: „Was soll ich wählen, Kafka oder RabbitMQ?“, „Welche Plattform ist besser?“. Die Wahrheit ist, dass es wirklich von Ihrer Situation, Ihrem derzeitigen Erfahrungsstand usw. abhängt. Ich möchte nicht übermäßig meinen Standpunkt äußern, da es eine zu große Vereinfachung wäre, eine einzige Plattform für alle Anwendungsfälle und möglichen Einschränkungen zu empfehlen. Ich habe diesen Artikelkreis geschrieben, um Ihnen zu helfen, sich eine eigene Meinung zu bilden.
Ich möchte anmerken, dass beide Systeme führend auf diesem Gebiet sind. Vielleicht bin ich etwas voreingenommen, denn aus der Erfahrung meiner Projekte neige ich dazu, Dinge wie die garantierte Reihenfolge von Nachrichten und Zuverlässigkeit mehr zu schätzen.
Ich beobachte andere Technologien, denen diese Zuverlässigkeit und die garantierte Reihenfolge fehlt, schaue dann auf RabbitMQ und Kafka — und erkenne den unglaublichen Wert beider Systeme.
Quelle: habr.com
