Wird beim Senden von Nachrichten an Kafka ein Schlüssel benötigt?

93
KeyedMessage<String, byte[]> keyedMessage = new KeyedMessage<String, byte[]>(request.getRequestTopicName(), SerializationUtils.serialize(message)); 
producer.send(keyedMessage);

Derzeit sende ich Nachrichten ohne Schlüssel als Teil von Schlüsselnachrichten. Funktioniert dies weiterhin delete.retention.ms? Muss ich einen Schlüssel als Teil der Nachricht senden? Ist es gut, Schlüssel als Teil der Nachricht zu machen?

gaurav
quelle

Antworten:

172

Schlüssel sind meistens nützlich / notwendig, wenn Sie eine starke Ordnung für einen Schlüssel benötigen und so etwas wie eine Zustandsmaschine entwickeln. Wenn Sie benötigen, dass Nachrichten mit demselben Schlüssel (z. B. einer eindeutigen ID) immer in der richtigen Reihenfolge angezeigt werden, wird durch Anhängen eines Schlüssels an Nachrichten sichergestellt, dass Nachrichten mit demselben Schlüssel immer an dieselbe Partition in einem Thema gesendet werden. Kafka garantiert die Reihenfolge innerhalb einer Partition, jedoch nicht über Partitionen in einem Thema hinweg. Wenn Sie also alternativ keinen Schlüssel angeben, was zu einer Round-Robin-Verteilung über Partitionen führt, wird diese Reihenfolge nicht beibehalten.

Im Fall einer Zustandsmaschine können Schlüssel mit log.cleaner.enable verwendet werden, um Einträge mit demselben Schlüssel zu deduplizieren . In diesem Fall geht Kafka davon aus, dass sich Ihre Anwendung nur um die letzte Instanz eines bestimmten Schlüssels kümmert und der Protokollbereiniger ältere Duplikate eines bestimmten Schlüssels nur löscht, wenn der Schlüssel nicht null ist. Diese Form der Protokollkomprimierung wird von der Eigenschaft log.cleaner.delete.retention gesteuert und erfordert Schlüssel.

Alternativ dazu löscht die standardmäßig aktivierte Eigenschaft log.retention.hours , die standardmäßig aktiviert ist, vollständige Segmente des Protokolls, die veraltet sind. In diesem Fall müssen keine Schlüssel angegeben werden. Kafka löscht einfach Teile des Protokolls, die älter als die angegebene Aufbewahrungsdauer sind.

Das ist alles zu sagen, wenn Sie die Protokollkomprimierung aktiviert haben oder eine strikte Reihenfolge für Nachrichten mit demselben Schlüssel benötigen, sollten Sie auf jeden Fall Schlüssel verwenden. Andernfalls bieten Nullschlüssel möglicherweise eine bessere Verteilung und verhindern potenzielle Hot-Spotting-Probleme, wenn einige Schlüssel häufiger als andere angezeigt werden.

kuujo
quelle
Ich bin neu in Kafka, das ist der Grund, warum ich so viele Fragen stelle: Es gibt einige Fragen dazu: Erste Frage: Können wir die Nachricht auf der Schlüsselbasis konsumieren? Derzeit konsumiere ich eine Nachricht von MessagAndMetadata mm. oder ist es in Ordnung, den Schlüssel zum Zeitpunkt des Nachrichtenverbrauchs zu ignorieren? Ich verwende eine hochgradige Consumer-API.
Gaurav
1
@kuujo Ich gehe davon aus, dass diese Deduplizierung nur für Protokolleinträge gilt und nicht unbedingt Nachrichten in einer Themenwarteschlange de-dupliziert.
user1658296
2
@oblivion, bei dem Nachrichten nacheinander in dieselbe Partition verschoben werden, ist wichtig für die Verarbeitung von Nicht-Idemponent-Updates, z. B. wenn der Kunde das Lieferdatum auswählt (eine Nachricht), die Meinung jedoch später ändert (zweite Nachricht). Wenn die Nachrichten zu verschiedenen Partitionen gehen sollen, kann jede Nachricht zuerst / zuletzt verarbeitet werden, z. B. mit 2 Verbrauchern, die von jeder Partition verbrauchen. Wenn beide Nachrichten, die sich auf dieselbe Zustellung beziehen, in dieselbe Partition gehen, werden sie First-In-First-Out verarbeitet und geben das richtige endgültige Zustelldatum an.
Kunal
3
Die Bestellgarantien stammen nicht vom Schlüssel, sondern von Nachrichten, die sich in derselben Partition befinden. Das Weiterleiten von Nachrichten an Partitionen muss nicht schlüsselbasiert sein. Sie können explizit eine Partition angeben, wenn Sie eineProducerRecord
Malt
2
Nach meinem Verständnis ist der Produzent-Client für die Auswahl der Partition ( kafka.apache.org/documentation.html#design_loadbalancing ) verantwortlich, die möglicherweise auf dem Schlüssel basiert oder nicht. Warum sind Ihrer Meinung nach Schlüssel für die Bestellung erforderlich?
lfk
5

Neben der sehr hilfreichen akzeptierten Antwort möchte ich noch einige Details hinzufügen

Partitionierung

Standardmäßig verwendet Kafka den Schlüssel der Nachricht, um die Partition des Themas auszuwählen, in das geschrieben wird. Dies geschieht durch so etwas wie

hash(key) % number_of_partitions

Wenn kein Schlüssel angegeben ist, partitioniert Kafka die Daten zufällig im Round-Robin-Verfahren.

Bestellung

Wie in der angegebenen Antwort angegeben, hat Kafka Garantien für die Bestellung der Nachrichten nur auf Partitionsebene.

Angenommen, Sie möchten Finanztransaktionen für Ihre Kunden in einem Kafka-Thema mit zwei Partitionen speichern. Die Nachrichten könnten so aussehen (Schlüssel: Wert)

null:{"customerId": 1, "changeInBankAccount": +200}
null:{"customerId": 2, "changeInBankAccount": +100}
null:{"customerId": 1, "changeInBankAccount": +200}
null:{"customerId": 1, "changeInBankAccount": -1337}
null:{"customerId": 1, "changeInBankAccount": +200}

Da wir keinen Schlüssel definiert haben, werden die beiden Partitionen vermutlich so aussehen

// partition 0
null:{"customerId": 1, "changeInBankAccount": +200}
null:{"customerId": 1, "changeInBankAccount": +200}
null:{"customerId": 1, "changeInBankAccount": +200}

// partition 1
null:{"customerId": 2, "changeInBankAccount": +100}
null:{"customerId": 1, "changeInBankAccount": -1337}

Ihr Verbraucher, der dieses Thema liest, könnte Ihnen am Ende mitteilen, dass der Kontostand zu einem bestimmten Zeitpunkt 600 beträgt, obwohl dies nie der Fall war! Nur weil alle Nachrichten in Partition 0 vor den Nachrichten in Partition 1 gelesen wurden.

Mit einem sinnvollen Schlüssel (wie customerId) könnte dies vermieden werden, da das Partitoning folgendermaßen aussehen würde:

// partition 0
1:{"customerId": 1, "changeInBankAccount": +200}
1:{"customerId": 1, "changeInBankAccount": +200}
1:{"customerId": 1, "changeInBankAccount": -1337}
1:{"customerId": 1, "changeInBankAccount": +200}

// partition 1
2:{"customerId": 2, "changeInBankAccount": +100}

Protokollverdichtung

Ohne Schlüssel als Teil Ihrer Nachrichten, werden Sie nicht in der Lage sein , das Thema Konfiguration einstellen cleanup.policyzu compacted. Laut Dokumentation "stellt die Protokollkomprimierung sicher, dass Kafka immer mindestens den letzten bekannten Wert für jeden Nachrichtenschlüssel im Datenprotokoll für eine einzelne Themenpartition beibehält."

Diese nette und hilfreiche Einstellung ist ohne Schlüssel nicht verfügbar.

Verwendung von Schlüsseln

In realen Anwendungsfällen kann der Schlüssel einer Kafka-Nachricht einen großen Einfluss auf Ihre Leistung und Klarheit Ihrer Geschäftslogik haben.

Ein Schlüssel kann zum Beispiel natürlich zum Partitionieren Ihrer Daten verwendet werden. Da Sie Ihre Konsumenten so steuern können, dass sie von bestimmten Partitionen lesen, kann dies als effizienter Filter dienen. Der Schlüssel kann auch einige Metadaten zum tatsächlichen Wert der Nachricht enthalten, mit denen Sie die nachfolgende Verarbeitung steuern können. Schlüssel sind normalerweise kleiner als Werte und es ist daher bequemer, einen Schlüssel anstelle des gesamten Werts zu analysieren. Gleichzeitig können Sie alle mit Ihrem Wert vorgenommenen Serialisierungen und Schema-Registrierungen auch mit dem Schlüssel anwenden.

Hinweis: Es gibt auch das Konzept des Headers , mit dem Informationen gespeichert werden können (siehe Dokumentation) .

Mike
quelle