
Wir haben die Übersetzung des nächsten Teils des Serienartikels vorbereitet, in dem die Funktionalität von Apache Kafka und RabbitMQ verglichen wird. Diese Veröffentlichung befasst sich mit Semantik- und Nachrichtenübermittlungsgarantien. Bitte beachten Sie, dass der Autor Kafka bis einschließlich Version 0.10 berücksichtigt hat und genau einmal in Version 0.11 erschienen ist. Trotzdem bleibt der Artikel relevant und enthält viele Punkte, die aus praktischer Sicht nützlich sind.
Vorherige Teile:
erstens ,
zweitens .
Sowohl RabbitMQ als auch Kafka bieten zuverlässige Garantien für die Zustellung von Nachrichten. Beide Plattformen bieten Garantien nach den Grundsätzen „höchstens einmalige Lieferung“ und „mindestens einmalige Lieferung“. Mit dem Prinzip „ausschließlich einmalige Lieferung“ gelten die Garantien von Kafka jedoch in einem sehr begrenzten Szenario.
Lassen Sie uns zunächst herausfinden, was diese Garantien bedeuten:
- Höchstens einmalige Lieferung. Dies bedeutet, dass die Nachricht nur einmal zugestellt werden kann. Die Nachricht kann jedoch verloren gehen.
- Mindestens einmalige Lieferung. Dies bedeutet, dass die Nachricht niemals verloren geht. In diesem Fall kann die Nachricht mehrmals zugestellt werden.
- Genau einmalige Lieferung. Der heilige Gral der Nachrichtensysteme. Alle Nachrichten werden ausschließlich einmal zugestellt.
Das Wort "Lieferung" ist hier wahrscheinlich kein genauer Begriff. Es wäre genauer, "Verarbeitung" zu sagen. In jedem Fall interessiert uns jetzt, ob der Verbraucher Nachrichten verarbeiten kann und nach welchem Prinzip dies geschieht: „nicht mehr als eine“, „mindestens eine“ oder „streng einmal“. Das Wort "Verarbeitung" erschwert jedoch die Wahrnehmung, und der Ausdruck "Zustellung nach dem Prinzip" streng einmal "" ist in diesem Fall keine genaue Definition, da die Nachricht möglicherweise zweimal zugestellt werden muss, um sie einmal ordnungsgemäß zu verarbeiten. Wenn der Empfänger während der Verarbeitung die Verbindung getrennt hat, muss die Nachricht erneut an den neuen Empfänger gesendet werden.
Der zweite. Wenn wir das Problem der Nachrichtenverarbeitung diskutieren, kommen wir zum Thema Teilfehler, was Entwicklern Kopfschmerzen bereitet. Bei der Verarbeitung der Nachricht gibt es mehrere Stufen. Es besteht aus Kommunikationssitzungen zwischen der Anwendung und dem Nachrichtensystem am Anfang und am Ende und der Anwendung selbst, die mit den Daten in der Mitte arbeitet. Teilanwendungsfehlerszenarien müssen von der Anwendung selbst behandelt werden. Wenn die ausgeführten Operationen vollständig transaktional sind und die Ergebnisse nach dem Prinzip „Alles oder Nichts“ formuliert sind, können Teilfehler in der Anwendungslogik vermieden werden. Viele Phasen umfassen jedoch häufig die Interaktion mit anderen Systemen, in denen Transaktionsmöglichkeiten unmöglich sind. Wenn wir die Beziehungen zwischen Nachrichtensystemen, Anwendungen, Cache und Datenbank in die Interaktion einbeziehen, können wir die Verarbeitung nur einmal garantieren? Die Antwort ist nein.
Die Strategie "streng einmal" beschränkt sich auf ein Szenario, in dem der einzige Empfänger verarbeiteter Nachrichten die Messaging-Plattform selbst ist und diese Plattform selbst vollständige Transaktionen bereitstellt. In diesem eingeschränkten Szenario können Sie Nachrichten verarbeiten, schreiben und Signale senden, dass sie im Rahmen einer Transaktion verarbeitet wurden, die nach dem Prinzip "Alles oder Nichts" durchgeführt wurde. Es wird von der Kafka Streams-Bibliothek bereitgestellt.
Wenn die Nachrichtenverarbeitung jedoch immer idempotent ist, können Sie vermeiden, dass die Strategie „nur einmal“ durch Transaktionen implementiert werden muss. Wenn die endgültige Verarbeitung von Nachrichten idempotent ist, können Sie sich Sorgen machen, Duplikate zu akzeptieren. Es können jedoch nicht alle Aktionen idempotent implementiert werden.
End-to-End-AlarmWas auf keinem Gerät aller Messagingsysteme, mit denen ich gearbeitet habe, dargestellt wird, ist eine End-to-End-Bestätigung. Da eine Nachricht in RabbitMQ in die Warteschlange gestellt werden kann, ist eine End-to-End-Benachrichtigung nicht sinnvoll. In Kafka können auf ähnliche Weise mehrere verschiedene Empfängergruppen gleichzeitig Informationen aus einem Thema lesen. Nach meiner Erfahrung sind End-to-End-Warnungen das, wonach Personen, die mit dem Konzept des Messaging noch nicht vertraut sind, häufig fragen. In solchen Fällen ist es am besten, sofort zu erklären, dass dies nicht möglich ist.
VerantwortungsketteIm Großen und Ganzen können Nachrichtenquellen nicht wissen, dass ihre Nachrichten an Empfänger übermittelt werden. Sie können nur wissen, dass das Nachrichtensystem ihre Nachrichten empfangen und die Verantwortung für die sichere Speicherung und Zustellung übernommen hat. Es gibt eine Verantwortungskette, die mit der Quelle beginnt, das Nachrichtensystem durchläuft und beim Empfänger endet. Jeder sollte seine Pflichten korrekt erfüllen und die Botschaft klar an den nächsten weitergeben. Dies bedeutet, dass Sie als Entwickler Ihre Anwendungen korrekt gestalten müssen, um den Verlust oder Missbrauch von Nachrichten zu verhindern, während diese unter Ihrer Kontrolle stehen.
Verfahren zur NachrichtenübertragungDieser Artikel befasst sich hauptsächlich damit, wie jede Plattform Sendestrategien "mindestens eine" und "nicht mehr als eine" bereitstellt. Es gibt jedoch noch eine Nachrichtenreihenfolge. In den vorherigen Teilen dieser Serie habe ich über die Reihenfolge, in der Nachrichten gesendet werden, und die Reihenfolge, in der sie verarbeitet werden, geschrieben. Ich rate Ihnen, auf diese Teile zu verweisen.
Kurz gesagt, sowohl RabbitMQ als auch Kafka bieten eine FIFO-Garantie (First In First Out). RabbitMQ behält diese Reihenfolge auf Warteschlangenebene und Kafka auf Segmentierungsebene bei. Die Auswirkungen solcher Entwurfsentscheidungen wurden in früheren Artikeln erörtert.
Liefergarantien in RabbitMQLiefergarantien werden gegeben:
- Nachrichtenzuverlässigkeit - Sie verschwinden nicht, während sie auf RabbitMQ gespeichert sind.
- Nachrichtenbenachrichtigungen - RabbitMQ tauscht Signale mit Absendern und Empfängern aus.
Zuverlässigkeitselemente
WarteschlangenspiegelungWarteschlangen können auf vielen Knoten (Servern) gespiegelt (repliziert) werden. Jede Warteschlange hat eine führende Warteschlange an einem der Knoten. Beispielsweise gibt es drei Knoten, 10 Warteschlangen und zwei Replikate pro Warteschlange. 10 Steuerwarteschlangen und 20 Replikate werden auf drei Knoten verteilt. Die Verteilung der Steuerwarteschlangen nach Knoten kann konfiguriert werden. Im Falle eines Knotenstopps:
- Anstelle jeder führenden Warteschlange auf dem blockierten Knoten wird eine Replik dieser Warteschlange auf einem anderen Knoten bereitgestellt.
- Auf anderen Knoten werden neue Replikate erstellt, um die verlorenen Replikate auf dem ausgehenden Knoten zu ersetzen und so den Replikationsfaktor zu unterstützen.
Das Problem der Fehlertoleranz wird im nächsten Teil des Artikels erörtert.
Vertrauenswürdige WarteschlangenEs gibt zwei Arten von Warteschlangen in RabbitMQ: zuverlässig und unzuverlässig. Zuverlässige Warteschlangen werden auf die Festplatte geschrieben und bei einem Neustart des Knotens gespeichert. Wenn der Knoten gestartet wird, werden sie überschrieben.
Dauerhafte BeiträgeWenn die Warteschlange zuverlässig ist, bedeutet dies nicht, dass ihre Nachrichten beim Neustart des Knotens gespeichert werden. Es werden nur Nachrichten wiederhergestellt, die vom Absender als dauerhaft markiert wurden.
Bei der Arbeit an RabbitMQ ist die mögliche Leistung umso geringer, je zuverlässiger die Nachricht ist. Wenn es einen Strom von Echtzeitereignissen gibt und es nicht kritisch ist, mehrere davon oder ein kleines Zeitintervall des Stroms zu verlieren, ist es besser, die Warteschlangenreplikation nicht zu verwenden und alle Nachrichten als instabil zu übertragen. Wenn es jedoch unerwünscht ist, Nachrichten aufgrund einer Fehlfunktion eines Knotens zu verlieren, ist es besser, zuverlässige Warteschlangen mit Replikation und stabilen Nachrichten zu verwenden.
Nachrichtenbenachrichtigungen
MessagingNachrichten können während der Übertragung verloren gehen oder dupliziert werden. Dies hängt vom Verhalten des Absenders ab.
"Geschossen und vergessen"Die Quelle kann beschließen, keine Bestätigung vom Empfänger anzufordern (Benachrichtigung über den Empfang einer Nachricht an den Absender) und die Nachricht einfach automatisch zu senden. Nachrichten werden nicht dupliziert, können jedoch verloren gehen (was der Strategie „maximal einmalige Zustellung“ entspricht).
Bestätigungen an den AbsenderWenn der Absender einen Kanal für den Warteschlangenbroker öffnet, kann er denselben Kanal zum Senden von Bestätigungen verwenden. Als Antwort auf die empfangene Nachricht muss der Warteschlangenbroker nun eines von zwei Dingen bereitstellen:
- basic.ack. Positive Bestätigung. Die Nachricht wird empfangen, die Verantwortung dafür liegt jetzt bei RabbitMQ;
- basic.nack. Negative Bestätigung. Es ist etwas passiert und die Nachricht wurde nicht verarbeitet. Die Verantwortung dafür bleibt bei der Quelle. Auf Wunsch kann er ein zweites Mal eine Nachricht senden.
Zusätzlich zu den positiven und negativen Zustellbenachrichtigungen wird eine basic.return-Nachricht bereitgestellt. Manchmal muss der Absender nicht nur wissen, dass die Nachricht in RabbitMQ angekommen ist, sondern auch, dass sie wirklich in eine oder mehrere Warteschlangen gefallen ist. Es kann vorkommen, dass die Quelle eine Nachricht an das Themenaustauschsystem sendet, in dem die Nachricht nicht an eine der Übermittlungswarteschlangen weitergeleitet wird. In dieser Situation verwirft der Broker die Nachricht einfach. In einigen Szenarien ist dies normal, in anderen muss die Quelle wissen, ob die Nachricht verworfen wurde, und entsprechend weiter vorgehen. Sie können das Flag "Obligatorisch" für einzelne Nachrichten setzen. Wenn die Nachricht in keiner Zustellwarteschlange definiert wurde, wird basic.return an den Absender zurückgegeben.
Die Quelle wartet möglicherweise nach dem Senden jeder Nachricht auf eine Bestätigung, dies verringert jedoch die Leistung erheblich. Stattdessen können Quellen einen stetigen Strom von Nachrichten senden, wodurch die Anzahl der nicht bestätigten Nachrichten begrenzt wird. Wenn das Limit für die Zwischennachricht erreicht ist, wird das Senden angehalten, bis alle Bestätigungen eingegangen sind.
Da jetzt viele Nachrichten vom Absender an RabbitMQ übertragen werden, werden Bestätigungen mithilfe des Mehrfachflags gruppiert, um die Leistung zu verbessern. Allen über den Kanal gesendeten Nachrichten wird ein monoton ansteigender ganzzahliger Wert zugewiesen, die „Sequenznummer“. Die Benachrichtigung einer Nachricht enthält die Sequenznummer der entsprechenden Nachricht. Und wenn gleichzeitig der Wert multiple = true ist, muss der Absender die Sequenznummern seiner Nachrichten verfolgen, um zu wissen, welche Nachrichten erfolgreich zugestellt wurden und welche nicht. Ich habe einen ausführlichen Artikel zu diesem Thema geschrieben.
Dank Bestätigungen vermeiden wir Nachrichtenverlust auf folgende Weise:
- erneutes Senden von Nachrichten im Falle einer negativen Benachrichtigung;
- Fortsetzung der Speicherung von Nachrichten irgendwo im Falle einer negativen Benachrichtigung oder einer grundlegenden Rückgabe.
TransaktionenTransaktionen werden in RabbitMQ aus folgenden Gründen selten verwendet:
- Schwache Garantien. Wenn Nachrichten an mehrere Warteschlangen weitergeleitet werden oder ein obligatorisches Symbol haben, wird die Transaktionskontinuität nicht unterstützt.
- Geringe Produktivität.
Ehrlich gesagt, ich habe sie nie verwendet, sie geben keine zusätzlichen Garantien, außer Bestätigungen an den Absender, und erhöhen nur die Unsicherheit in der Frage, wie die Bestätigung des Empfangs von Nachrichten zu interpretieren ist, die sich aus dem Abschluss von Transaktionen ergeben.
Kommunikations- / KanalfehlerZusätzlich zu den Benachrichtigungen über den Empfang von Nachrichten muss der Absender die Fehler von Kommunikationstools und Brokern berücksichtigen. Beide Faktoren führen zum Verlust des Kommunikationskanals. Mit dem Verlust von Kanälen verschwindet die Möglichkeit, noch nicht empfangene Benachrichtigungen über den Empfang von Nachrichten zu erhalten. Hier muss der Absender zwischen dem Risiko eines Nachrichtenverlusts und dem Risiko einer Vervielfältigung wählen.
Ein Brokerfehler kann auftreten, wenn sich die Nachricht im Betriebssystempuffer befand oder vorverarbeitet wurde und die Nachricht dann verloren geht. Oder vielleicht wurde die Nachricht in die Warteschlange gestellt, aber der Nachrichtenbroker ist vor dem Senden einer Bestätigung gestorben. In diesem Fall wird die Nachricht erfolgreich zugestellt.
Ebenso wirkt sich der Ausfall der Kommunikationsmittel auf die Situation aus. Ist während der Übertragung der Nachricht ein Fehler aufgetreten? Oder nachdem die Nachricht in die Warteschlange gestellt wurde, aber bevor eine positive Benachrichtigung empfangen wurde?
Der Absender kann dies nicht feststellen und muss daher eine der folgenden Optionen auswählen:
- Leiten Sie die Nachricht nicht weiter, da sonst das Risiko eines Verlusts besteht.
- Senden Sie die Nachricht erneut und es besteht die Gefahr von Duplikaten.
Wenn viele Absendernachrichten übertragen werden, wird das Problem komplizierter. Der Absender kann den Empfängern nur einen Hinweis geben, indem er der Nachricht einen speziellen Header hinzufügt, der angibt, dass die Nachricht ein zweites Mal gesendet wird. Empfänger können entscheiden, Nachrichten auf das Vorhandensein solcher Header zu überprüfen und, falls sie gefunden werden, zusätzlich nach empfangenen Nachrichten auf Duplikate zu suchen (falls eine solche Überprüfung zuvor nicht durchgeführt wurde).
Empfänger
Empfängern stehen zwei Optionen zum Empfangen von Benachrichtigungen zur Verfügung:
- kein Benachrichtigungsmodus;
- manueller Benachrichtigungsmodus.
Kein BenachrichtigungsmodusEr ist ein Modus für automatische Benachrichtigungen. Und er ist gefährlich. Erstens, denn wenn eine Nachricht in Ihre Anwendung gelangt, wird sie aus der Warteschlange entfernt. Dies kann zu Nachrichtenverlust führen, wenn:
- Die Verbindung wurde unterbrochen, bevor die Nachricht empfangen wurde.
- Die Nachricht befindet sich noch im internen Puffer und die Anwendung wurde deaktiviert.
- Nachrichtenverarbeitung fehlgeschlagen.
Darüber hinaus verlieren wir Gegendruckmechanismen, um die Qualität der Nachrichtenübermittlung zu überwachen. Durch Einstellen des Modus zum manuellen Senden von Benachrichtigungen können Sie einen Prefetch (oder die Stufe der bereitgestellten Dienste, QoS) festlegen, um die einmalige Anzahl von Nachrichten zu begrenzen, deren Empfang vom System noch nicht bestätigt wurde. Ohne dies sendet RabbitMQ Nachrichten so schnell, wie es die Verbindung zulässt, und dies kann schneller sein, als der Empfänger sie verarbeiten kann. Infolgedessen sind die Puffer voll und es treten Speicherfehler auf.
Manueller BenachrichtigungsmodusDer Empfänger muss manuell eine Benachrichtigung über den Empfang jeder Nachricht senden. Er kann einen Prefetch festlegen, falls die Anzahl der Nachrichten mehr als eins beträgt, und viele Nachrichten gleichzeitig verarbeiten. Er kann entscheiden, für jede Nachricht eine Benachrichtigung zu senden, oder er kann das Mehrfachflag anwenden und mehrere Benachrichtigungen gleichzeitig senden. Das Gruppieren von Benachrichtigungen verbessert die Leistung.
Wenn der Empfänger den Kanal öffnet, enthalten die Nachrichten, die ihn durchlaufen, den Parameter Delivery Tag, dessen Wert eine ganzzahlige, monoton ansteigende Zahl ist. Es ist in jeder Empfangsbestätigung enthalten und wird als Nachrichtenkennung verwendet.
Benachrichtigungen können wie folgt sein:
- basic.ack. Danach entfernt RabbitMQ die Nachricht aus der Warteschlange. Das Mehrfachflag kann hier angewendet werden.
- basic.nack. Der Empfänger muss ein Flag setzen, um RabbitMQ mitzuteilen, ob die Nachricht erneut in die Warteschlange gestellt werden soll. Beim Zurücksetzen geht die Nachricht an den Anfang der Warteschlange. Von dort wird es erneut an den Empfänger gesendet (sogar an denselben Empfänger). Die basic.nack-Benachrichtigung unterstützt das Mehrfachflag.
- basic.reject. Entspricht basic.nack, unterstützt jedoch nicht das Mehrfachflag.
Somit sind semantisch basic.ack und basic.nack mit Requeue = false identisch. Beide Operatoren bedeuten das Entfernen einer Nachricht aus der Warteschlange.
Die nächste Frage ist, wann Empfangsbestätigungen gesendet werden sollen. Wenn die Nachricht schnell verarbeitet wurde, möchten Sie möglicherweise sofort nach Abschluss dieses Vorgangs eine Benachrichtigung senden (erfolgreich oder nicht erfolgreich). Aber wenn sich die Nachricht in der RabbitMQ-Warteschlange befand und die Verarbeitung viele Minuten dauert? Das Senden einer Benachrichtigung danach ist problematisch, da beim Schließen des Kanals alle Nachrichten, an die keine Benachrichtigungen gesendet wurden, an die Warteschlange zurückgegeben werden und das Senden ein zweites Mal erfolgt.
Verbindungs- / NachrichtenbrokerfehlerWenn die Verbindung getrennt wurde oder ein Fehler im Broker aufgetreten ist, nach dem der Kanal nicht mehr funktioniert, werden alle Nachrichten, deren Empfang nicht bestätigt wurde, erneut in die Warteschlange gestellt und weitergeleitet. Dies ist gut, weil es Datenverlust verhindert, aber schlecht, weil es übermäßige Duplizierung verursacht.
Je länger der Empfänger über einen längeren Zeitraum Nachrichten hat, deren Empfang er nicht bestätigt hat, desto höher ist das Risiko einer Weiterleitung. Wenn eine Nachricht erneut gesendet wird, wird RabbitMQ für das Weiterleitungsflag auf true gesetzt. Aus diesem Grund hat der Empfänger zumindest einen Hinweis darauf, dass die Nachricht möglicherweise bereits verarbeitet wurde.
IdempotenzWenn eine Idempotenz erforderlich ist und garantiert, dass keine Nachricht verloren geht, sollten einige doppelte Überprüfungen oder andere idempotente Schemata eingebaut werden. Wenn das Überprüfen auf doppelte Nachrichten zu teuer ist, können Sie eine Strategie anwenden, bei der der Absender immer einen speziellen Header zum erneuten Senden von Nachrichten hinzufügt und der Empfänger die empfangenen Nachrichten auf das Vorhandensein eines solchen Headers und eines erneuten Sendeflags überprüft.
Fazit
RabbitMQ bietet zuverlässige, langfristige Messaging-Garantien, aber es gibt viele Situationen, in denen sie nicht helfen.
Hier ist eine Liste von Punkten, an die Sie sich erinnern sollten:
- Sie sollten Warteschlangenspiegelung, zuverlässige Warteschlangen, dauerhafte Nachrichten, Bestätigungen für den Absender, ein Bestätigungsflag und eine erzwungene Benachrichtigung des Empfängers verwenden, wenn zuverlässige Garantien für die Strategie "mindestens einmalige Zustellung" erforderlich sind.
- Wenn das Senden im Rahmen der Strategie „mindestens einmalige Zustellung“ durchgeführt wird, müssen Sie möglicherweise einen Mechanismus für die Deduplizierung oder Idempotenz hinzufügen, wenn Sie die gesendeten Daten duplizieren.
- Wenn das Problem des Nachrichtenverlusts nicht so wichtig ist wie das Problem der Zustellgeschwindigkeit und der hohen Skalierbarkeit, denken Sie an Systeme ohne Redundanz, ohne persistente Nachrichten und ohne Bestätigung auf der Quellseite. Trotzdem würde ich es vorziehen, erzwungene Benachrichtigungen vom Empfänger zu hinterlassen, um den Fluss empfangener Nachrichten durch Ändern der Prefetch-Einschränkungen zu steuern. In diesem Fall müssen Sie Benachrichtigungen stapelweise senden und das Flag "Mehrfach" verwenden.
Liefergarantien in KafkaLiefergarantien werden gegeben:
- Nachrichtenbeständigkeit - In einem Segment gespeicherte Nachrichten gehen nicht verloren.
- Nachrichtenbenachrichtigungen - der Austausch von Signalen zwischen Kafka (und möglicherweise dem Apache Zookeeper-Repository) einerseits und der Quelle / dem Empfänger andererseits.
Zwei Worte zur NachrichtenverpackungEiner der Unterschiede zwischen RabbitMQ und Kafka ist die Verwendung von Paketen für Messaging.
RabbitMQ bietet etwas Ähnliches wie Verpackung dank:
- Unterbrechen Sie das Senden aller X-Nachrichten, bis alle Benachrichtigungen empfangen wurden. RabbitMQ gruppiert Benachrichtigungen normalerweise mit dem Flag "multiple".
- «prefetch» «multiple».
. “multiple”. TCP.
Kafka . , . RabbitMQ, , , . , .
Kafka , , . , . RabbitMQ API , . RabbitMQ .
,Kafka - , , . . , , , , , .
Kafka (In Sync Replicas, ISR). . , , ( 10 ). , . - , .. . .
, Kafka , , , Kafka .
, Kafka, , :
- , . Acks=0.
- . Acks=1
- . Acks=All
, RabbitMQ. , , ( , ). , .
Kafka . :
- enable.idempotence “true”,
- max.in.flight.requests.per.connection 5 ,
- retries 1 ,
- acks “all”.
, acks=0/1 , .
, , . ZooKeeper Kafka.
(), , :
- In regelmäßigen Abständen. . . . , .
- , . “ ”. , ; , . , 10 , 4 , , , ;
- , . “ ”. , , , . , 10 , , 4 ;
- . , .
“ ” Kafka Streams, Java. Java . “ ”, , , . , , “ ” . , , () .
, Kafka Streams, , , “ ”. Kafka: . , . , , , ( ), .
Kafka “--”. . , , .
“ ”, , (, , ). “ ”, , . .
: “ ” ? . , , . . (Last Stable Offset, LSO) — ; “ ” .
Schlussfolgerungen. , , . , Kafka , .
Zusammenfassend
- “ ” “ ”.
- .
- , . Kafka , .
- , , .
- .
- Kafka , “--”. .
- Kafka, - , ( ). RabbitMQ .
- Kafka kann die Vorteile der Paketierung aufgrund seiner Paketverteilungsfunktionen erhöhen, und RabbitMQ verfügt aufgrund eines passiven Empfangsmodells, das Empfängerkonflikte nicht verhindert, nicht über eine Paketierung.