Kapitel 1. Treffen mit Kafka

Diese Arbeit wurde mithilfe von KI übersetzt. Wir freuen uns über dein Feedback und deine Kommentare: translation-feedback@oreilly.com

Jedes Unternehmen wird von Daten angetrieben. Wir nehmen Informationen auf, analysieren sie, bearbeiten sie und erzeugen weitere Daten als Output. Jede Anwendung erzeugt Daten, egal ob es sich um Protokollmeldungen, Messwerte, Benutzeraktivitäten, ausgehende Nachrichten oder etwas anderes handelt. Jedes Byte an Daten hat eine Geschichte zu erzählen, etwas Wichtiges, das für die nächsten Schritte von Bedeutung ist. Um das herauszufinden, müssen wir die Daten von dort, wo sie entstehen, dorthin bringen, wo sie analysiert werden können. Wir sehen das jeden Tag auf Websites wie Amazon, wo unsere Klicks auf Artikel, die uns interessieren, in Empfehlungen umgewandelt werden, die uns wenig später angezeigt werden.

Je schneller wir dies tun können, desto flexibler und reaktionsschneller können unsere Organisationen sein. Je weniger Aufwand wir mit dem Verschieben von Daten haben, desto mehr können wir uns auf unser Kerngeschäft konzentrieren. Aus diesem Grund ist die Pipeline eine entscheidende Komponente im datengesteuerten Unternehmen. Die Art und Weise, wie wir die Daten bewegen, ist fast genauso wichtig wie die Daten selbst.

Wenn Wissenschaftler sich nicht einig sind, liegt das daran, dass wir nicht genügend Daten haben. Dann können wir uns darauf einigen, welche Art von Daten wir brauchen; wir bekommen die Daten, und die Daten lösen das Problem. Entweder habe ich Recht, oder du hast Recht, oder wir haben beide Unrecht. Und wir machen weiter.

Neil deGrasse Tyson

Veröffentlichen/Abonnieren von Nachrichten

Bevor wir auf die Besonderheiten von Apache Kafka eingehen, ist es wichtig, das Konzept des Publish/Subscribe Messaging zu verstehen und zu begreifen, warum es eine wichtige Komponente datengesteuerter Anwendungen ist. Publish/Subscribe (Pub/Sub) Messaging ist ein Muster, das dadurch gekennzeichnet ist, dass der Absender (Publisher) einer Dateneinheit (Nachricht) diese nicht direkt an einen Empfänger weiterleitet. Stattdessen klassifiziert der Herausgeber die Nachricht auf irgendeine Weise, und der Empfänger (Abonnent) abonniert den Empfang bestimmter Nachrichtenklassen. Pub/Sub-Systeme haben oft einen Broker, einen zentralen Punkt, an dem Nachrichten veröffentlicht werden, um dieses Muster zu erleichtern.

Wie es anfängt

Viele Anwendungsfälle für Publish/Subscribe beginnen auf die gleiche Weise: mit einer einfachen Nachrichtenwarteschlange oder einem prozessübergreifenden Kommunikationskanal. Wenn du zum Beispiel eine Anwendung erstellst, die Überwachungsdaten an eine andere Stelle senden muss, öffnest du eine direkte Verbindung von deiner Anwendung zu einer App, die deine Metriken auf einem Dashboard anzeigt, und schickst die Metriken über diese Verbindung, wie in Abbildung 1-1 dargestellt.

kdg2 0101
Abbildung 1-1. Ein einzelner, direkter Metrikenverlag

Dies ist eine einfache Lösung für ein einfaches Problem, das funktioniert, wenn du mit dem Monitoring anfängst. Schon bald stellst du fest, dass du deine Kennzahlen über einen längeren Zeitraum analysieren möchtest, und das funktioniert im Dashboard nicht gut. Du startest einen neuen Dienst, der Metriken empfangen, speichern und analysieren kann. Um dies zu unterstützen, änderst du deine Anwendung so, dass sie Metriken in beide Systeme schreibt. Inzwischen hast du drei weitere Anwendungen, die Metriken generieren, und sie alle stellen dieselben Verbindungen zu diesen beiden Diensten her. Dein Kollege meint, dass es eine gute Idee wäre, die Dienste auch aktiv abzufragen, um Benachrichtigungen zu erhalten, also fügst du jeder Anwendung einen Server hinzu, der auf Anfrage Metriken liefert. Nach einer Weile hast du mehr Anwendungen, die diese Server nutzen, um individuelle Metriken zu erhalten und sie für verschiedene Zwecke zu verwenden. Diese Architektur kann ähnlich aussehen wie in Abbildung 1-2, mit Verbindungen, die noch schwieriger zu verfolgen sind.

kdg2 0102
Abbildung 1-2. Viele Herausgeber von Metriken, die direkte Verbindungen nutzen

Die technische Schuld, die hier aufgebaut wurde, ist offensichtlich, also beschließt du, einen Teil davon zurückzuzahlen. Du richtest eine einzige Anwendung ein, die Metriken von allen Anwendungen empfängt, und stellst einen Server bereit, der diese Metriken für jedes System abfragt, das sie benötigt. Dadurch wird die Komplexität der Architektur auf etwas Ähnliches wie in Abbildung 1-3 reduziert. Glückwunsch, du hast ein Publish/Subscribe-Messaging-System gebaut!

kdg2 0103
Abbildung 1-3. Ein Publish/Subscribe-System für Metriken

Individuelle Warteschlangensysteme

Zur gleichen Zeit, in der du diesen Krieg mit den Metriken geführt hast, hat einer deinerMitarbeiter ähnliche Arbeit mit den Logmeldungen geleistet. Ein anderer hat daran gearbeitet, das Nutzerverhalten auf der Frontend-Website zu verfolgen und diese Informationen an Entwickler weiterzugeben, die an maschinellem Lernen arbeiten, sowie einige Berichte für das Management zu erstellen. Ihr seid alle einen ähnlichen Weg gegangen und habt Systeme aufgebaut, die die Herausgeber der Informationen von den Abonnenten dieser Informationen entkoppeln. Abbildung 1-4 zeigt eine solche Infrastruktur mit drei separaten Pub/Sub-Systemen.

kdg2 0104
Abbildung 1-4. Mehrere Publish/Subscribe-Systeme

Das ist sicherlich viel besser als die Verwendung von Punkt-zu-Punkt-Verbindungen (wie in Abbildung 1-2), aber es gibt eine Menge Doppelarbeit. Dein Unternehmen unterhält mehrere Systeme für die Warteschlangenverwaltung, die alle ihre eigenen Fehler und Einschränkungen haben. Du weißt auch, dass es bald mehr Anwendungsfälle für Messaging geben wird. Du hättest gerne ein einziges zentrales System, das die Veröffentlichung allgemeiner Datentypen ermöglicht, die mit dem Wachstum deines Unternehmens mitwachsen werden.

Kafka eingeben

Apache Kafka wurde als Publish/Subscribe-Messaging-System entwickelt, um dieses Problem zu lösen. Es wird oft als "verteiltes Commit-Log" oder neuerdings auch als "verteilte Streaming-Plattform" bezeichnet. Ein Commit-Log eines Dateisystems oder einer Datenbank dient dazu, eine dauerhafte Aufzeichnung aller Transaktionen zu erstellen, damit sie wiedergegeben werden können, um den Zustand eines Systems konsistent zu gestalten. In ähnlicher Weise werden die Daten in Kafka dauerhaft und geordnet gespeichert und können deterministisch gelesen werden. Außerdem können die Daten innerhalb des Systems verteilt werden, was zusätzlichen Schutz vor Ausfällen bietet und die Leistung erheblich steigert.

Nachrichten und Chargen

Die Einheit der Daten in Kafka wird Nachricht genannt. Wenn du von einem Datenbank-Hintergrund her an Kafka herangehst, kannst du dir das ähnlich wie eine Zeile oder einen Datensatz vorstellen. Für Kafka ist eine Nachricht einfach ein Array von Bytes, d.h. die darin enthaltenen Daten haben für Kafka kein bestimmtes Format oder eine bestimmte Bedeutung. Eine Nachricht kann einen optionalen Teil der Metadaten enthalten, der als Schlüssel bezeichnet wird. Der Schlüssel ist ebenfalls ein Byte-Array und hat, genau wie die Nachricht, keine besondere Bedeutung für Kafka. Schlüssel werden verwendet, wenn Nachrichten auf kontrollierte Weise in Partitionen geschrieben werden sollen. Das einfachste Verfahren besteht darin, einen konsistenten Hash des Schlüssels zu erzeugen und dann die Partitionsnummer für die Nachricht auszuwählen, indem das Ergebnis des Hashes mit der Gesamtzahl der Partitionen im Topic multipliziert wird. So wird sichergestellt, dass Nachrichten mit demselben Schlüssel immer in dieselbe Partition geschrieben werden (vorausgesetzt, die Anzahl der Partitionen ändert sich nicht).

Um effizient zu sein, werden die Nachrichten in Kafka in Batches geschrieben. Ein Batch ist eine Sammlung von Nachrichten, die alle für dasselbe Thema und dieselbe Partition erstellt werden. Ein einzelner Round Trip über das Netzwerk für jede Nachricht würde zu einem übermäßigen Overhead führen, und das Sammeln von Nachrichten in einem Batch reduziert diesen. Das ist natürlich ein Kompromiss zwischen Latenz und Durchsatz: Je größer die Batches sind, desto mehr Nachrichten können pro Zeiteinheit verarbeitet werden, aber desto länger braucht eine einzelne Nachricht, um sich zu verbreiten. Stapel werden in der Regel auch komprimiert, was eine effizientere Datenübertragung und Speicherung ermöglicht, allerdings auf Kosten einer gewissen Verarbeitungsleistung. Sowohl Schlüssel als auch Stapel werden in Kapitel 3 näher erläutert.

Schemata

Während Nachrichten für Kafka selbst undurchsichtige Byte-Arrays sind, empfiehlt es sich, den Nachrichteninhalt mit einer zusätzlichen Struktur, einem Schema, zu versehen, damit er leicht zu verstehen ist. Es gibt viele Optionen für das Nachrichtenschema, je nach den individuellen Anforderungen deiner Anwendung. Einfachere Systeme wie JavaScript Object Notation (JSON) und Extensible Markup Language (XML) sind leicht zu handhaben und für den Menschen lesbar. Allerdings fehlen ihnen Funktionen wie eine robuste Typverarbeitung und Kompatibilität zwischen verschiedenen Schemaversionen. Viele Kafka-Entwickler bevorzugen die Verwendung von Apache Avro, einem Serialisierungsframework, das ursprünglich für Hadoop entwickelt wurde. Avro bietet ein kompaktes Serialisierungsformat, Schemas, die von den Nutzdaten getrennt sind und bei Änderungen keinen Code erfordern, sowie eine starke Datentypisierung und Schemaentwicklung, die sowohl rückwärts als auch vorwärts kompatibel ist.

Ein einheitliches Datenformat ist in Kafka wichtig, da es die Entkopplung von Schreiben und Lesen von Nachrichten ermöglicht. Wenn diese Aufgaben eng miteinander gekoppelt sind, müssen die Anwendungen, die Nachrichten abonnieren, aktualisiert werden, um das neue Datenformat parallel zum alten Format zu verarbeiten. Erst dann können die Anwendungen, die die Nachrichten veröffentlichen, aktualisiert werden, um das neue Format zu nutzen. Durch die Verwendung klar definierter Schemata und deren Speicherung in einem gemeinsamen Repository können die Nachrichten in Kafka ohne Koordination verstanden werden. Schemas und Serialisierung werden in Kapitel 3 ausführlicher behandelt.

Themen und Partitionen

Nachrichten in Kafka werden in Topics kategorisiert. Die engsten Entsprechungen für ein Topic sind eine Datenbanktabelle oder ein Ordner in einem Dateisystem. Themen werden zusätzlich in eine Reihe von Partitionen unterteilt. Um auf die Beschreibung des "Commit-Logs" zurückzukommen: Eine Partition ist ein einzelnes Protokoll. Die Nachrichten werden nur angehängt und in der Reihenfolge vom Anfang bis zum Ende gelesen. Da ein Thema in der Regel mehrere Partitionen hat, gibt es keine Garantie für die Reihenfolge der Meldungen im gesamten Thema, sondern nur innerhalb einer einzelnenPartition. Abbildung 1-5 zeigt ein Thema mit vier Partitionen, an deren Ende jeweils eine Nachricht angehängt ist. Partitionen sind auch die Art und Weise, wie Kafka Redundanz und Skalierbarkeit bietet. Jede Partition kann auf einem anderen Server gehostet werden, was bedeutet, dass ein einzelnes Topic horizontal über mehrere Server skaliert werden kann, um eine Leistung zu erzielen, die weit über die Möglichkeiten eines einzelnen Servers hinausgeht. Außerdem können Partitionen repliziert werden, so dass verschiedene Server eine Kopie der gleichen Partition speichern, falls ein Server fehlschlägt.

kdg2 0105
Abbildung 1-5. Darstellung eines Themas mit mehreren Partitionen

Der Begriff Stream wird oft verwendet, wenn es um Daten in Systemen wie Kafka geht. Meistens wird ein Stream als ein einziges Thema von Daten betrachtet, unabhängig von der Anzahl der Partitionen. Er stellt einen einzelnen Datenstrom dar, der sich von den Produzenten zu den Konsumenten bewegt. Diese Art, sich auf Nachrichten zu beziehen, ist am gebräuchlichsten, wenn es um die Stream-Verarbeitung geht, d. h. wenn Frameworks - wie Kafka Streams, Apache Samza und Storm - die Nachrichten in Echtzeit verarbeiten. Diese Arbeitsweise ist vergleichbar mit der Art und Weise, wie Offline-Frameworks, insbesondere Hadoop, mit Massendaten zu einem späteren Zeitpunkt arbeiten. Einen Überblick über die Stream-Verarbeitung findest du in Kapitel 14.

Erzeuger und Verbraucher

Kafka-Clients sind Nutzer des Systems. Es gibt zwei Grundtypen: Produzenten und Konsumenten. Außerdem gibt es erweiterte Client-APIs: Kafka Connect API für die Datenintegration und Kafka Streams für die Stream-Verarbeitung. Die fortgeschrittenen Clients nutzen Producer und Consumer als Bausteine und bieten darüber hinaus weitere Funktionen.

Erzeuger erstellen neue Nachrichten. In anderen Publish/Subscribe-Systemen werden sie auch Publisher oder Writer genannt. Eine Nachricht wird für ein bestimmtes Topic produziert. Standardmäßig verteilt der Producer die Nachrichten gleichmäßig auf alle Partitionen eines Topics. In manchen Fällen kann der Producer Nachrichten an bestimmte Partitionen leiten. Dies geschieht in der Regel mithilfe des Nachrichtenschlüssels und eines Partitionierers, der einen Hash des Schlüssels erzeugt und ihn einer bestimmten Partition zuordnet. So wird sichergestellt, dass alle Nachrichten, die mit einem bestimmten Schlüssel erzeugt werden, in dieselbe Partition geschrieben werden. Der Produzent kann auch einen benutzerdefinierten Partitionierer verwenden, der anderen Geschäftsregeln für die Zuordnung von Nachrichten zu Partitionen folgt. Auf Produzenten wird in Kapitel 3 näher eingegangen.

Verbraucher lesen Nachrichten. In anderen Publish/Subscribe-Systemen werden diese Kunden als Abonnenten oder Leser bezeichnet. Der Verbraucher abonniert ein oder mehrere Themen und liest die Nachrichten in der Reihenfolge, in der sie für jede Partition erstellt wurden. Der Verbraucher behält den Überblick darüber, welche Nachrichten er bereits konsumiert hat, indem er den Offset der Nachrichten verfolgt. Der Offset - einganzzahliger Wert, der kontinuierlich ansteigt - ist ein weiterer Teil der Metadaten, die Kafka jeder Nachricht hinzufügt, wenn sie produziert wird. Jede Nachricht in einer bestimmten Partition hat einen eindeutigen Offset, und die folgende Nachricht hat einen größeren Offset (der allerdings nicht unbedingt monoton größer ist). Indem der nächstmögliche Offset für jede Partition gespeichert wird, typischerweise in Kafka selbst, kann ein Konsument anhalten und neu starten, ohne seinen Platz zu verlieren.

Die Verbraucher arbeiten als Teil einer Verbrauchergruppe, d.h. ein oder mehrere Verbraucher, die zusammenarbeiten, um ein Thema zu konsumieren. Die Gruppe stellt sicher, dass jede Partition nur von einem Mitglied konsumiert wird. In Abbildung 1-6 befinden sich drei Verbraucher in einer Gruppe, die ein Thema konsumieren. Zwei der Verbraucher arbeiten jeweils von einer Partition aus, während der dritte Verbraucher von zwei Partitionen aus arbeitet. Die Zuordnung eines Verbrauchers zu einer Partition wird oft als Besitz der Partition durch den Verbraucher bezeichnet.

Auf diese Weise können die Verbraucher horizontal skalieren, um Themen mit einer großen Anzahl von Nachrichten zu konsumieren. Wenn ein einzelner Verbraucher fehlschlägt, ordnen die übrigen Mitglieder der Gruppe die konsumierten Partitionen neu zu, um für das fehlende Mitglied einzuspringen. Verbraucher und Verbrauchergruppen werden in Kapitel 4 ausführlicher behandelt.

kdg2 0106
Abbildung 1-6. Eine Verbrauchergruppe liest aus einem Thema

Makler und Cluster

Ein einzelner Kafka-Server wird als Broker bezeichnet. Der Broker empfängt Nachrichten von Produzenten, ordnet ihnen Offsets zu und schreibt die Nachrichten in die Speicherung auf der Festplatte. Er bedient auch die Konsumenten, indem er auf Abrufanfragen für Partitionen antwortet und die veröffentlichtenNachrichten zurückschickt. Je nach Hardware und Leistungsmerkmalen kann ein einzelner Broker leicht Tausende von Partitionen und Millionen von Nachrichten pro Sekunde verarbeiten.

Kafka-Broker sind für den Betrieb als Teil eines Clusters konzipiert. Innerhalb eines Clusters von Brokern fungiert ein Broker auch als Cluster-Controller (der automatisch von den aktiven Mitgliedern des Clusters gewählt wird). Der Controller ist für die Verwaltung zuständig, z. B. für die Zuweisung von Partitionen an Broker und die Überwachung von Brokerausfällen. Eine Partition gehört einem einzelnen Broker im Cluster und dieser Broker wird als Leader der Partition bezeichnet. Eine replizierte Partition (wie in Abbildung 1-7 zu sehen) wird weiteren Brokern zugewiesen, den sogenannten Followern der Partition. Die Replikation sorgt für Redundanz der Nachrichten in der Partition, so dass einer der Follower die Führung übernehmen kann, wenn ein Broker ausfällt. Alle Produzenten müssen sich mit dem Leader verbinden, um Nachrichten zu veröffentlichen, aber die Konsumenten können sie entweder vom Leader oder von einem der Follower abrufen. Clusteroperationen, einschließlich der Partitionsreplikation, werden in Kapitel 7 ausführlich behandelt.

kdg2 0107
Abbildung 1-7. Replikation von Partitionen in einem Cluster

Eine wichtige Funktion von Apache Kafka ist die Speicherung, d. h. die dauerhafte Speicherung von Nachrichten für einen bestimmten Zeitraum. Kafka-Broker sind mit einer Standardeinstellung für die Aufbewahrung von Topics konfiguriert, die entweder Nachrichten für einen bestimmten Zeitraum (z. B. 7 Tage) oder bis zum Erreichen einer bestimmten Größe in Bytes (z. B. 1 GB) aufbewahrt. Sobald diese Grenzen erreicht sind, werden die Nachrichten gelöscht. Auf diese Weise legt die Aufbewahrungskonfiguration eine Mindestmenge an Daten fest, die jederzeit verfügbar ist. Einzelne Themen können auch mit eigenen Aufbewahrungseinstellungen konfiguriert werden, damit die Nachrichten nur so lange gespeichert werden, wie sie nützlich sind. So kann z. B. ein Tracking-Thema mehrere Tage aufbewahrt werden, während Anwendungsmetriken nur wenige Stunden aufbewahrt werden können. Themen können auch als "log compacted" konfiguriert werden, was bedeutet, dass Kafka nur die letzte mit einem bestimmten Schlüssel erstellte Nachricht aufbewahrt. Dies kann für Daten vom Typ Changelog nützlich sein, bei denen nur die letzte Aktualisierung von Interesse ist.

Mehrere Cluster

Wenn Kafka-Einsätze wachsen, ist es oft von Vorteil, mehrere Cluster zu haben. Es gibt mehrere Gründe, warum dies nützlich sein kann:

  • Trennung der Datentypen

  • Isolierung für Sicherheitsanforderungen

  • Mehrere Rechenzentren (Disaster Recovery)

Vor allem bei der Arbeit mit mehreren Rechenzentren ist es oft erforderlich, dass Nachrichten zwischen ihnen kopiert werden. Auf diese Weise können Online-Anwendungen Zugriff auf die Nutzeraktivitäten an beiden Standorten haben. Wenn zum Beispiel ein/e Nutzer/in öffentliche Informationen in seinem/ihrem Profil ändert, muss diese Änderung unabhängig von dem Rechenzentrum, in dem die Suchergebnisse angezeigt werden, sichtbar sein. Oder es können Überwachungsdaten von vielen Standorten an einem einzigen zentralen Ort gesammelt werden, wo die Analyse- und Warnsysteme gehostet werden. Die Replikationsmechanismen in den Kafka-Clustern funktionieren nur innerhalb eines einzelnen Clusters, nicht zwischen mehreren Clustern.

Das Kafka-Projekt umfasst ein Tool namens MirrorMaker, das für die Replikation von Daten auf andere Cluster verwendet wird. Im Kern ist MirrorMaker einfach ein Kafka-Konsument und -Produzent, die mit einer Warteschlange verbunden sind. Nachrichten werden von einem Kafka-Cluster konsumiert und für einen anderen produziert. Abbildung 1-8 zeigt ein Beispiel für eine Architektur, die MirrorMaker nutzt, um Nachrichten von zwei lokalen Clustern zu einem Gesamtcluster zu aggregieren und diesen Cluster dann in andere Rechenzentren zu kopieren. Die Einfachheit der Anwendung täuscht darüber hinweg, wie leistungsfähig sie bei der Erstellung anspruchsvoller Datenpipelines ist, auf die in Kapitel 9 näher eingegangen wird.

kdg2 0108
Abbildung 1-8. Architektur mit mehreren Rechenzentren

Warum Kafka?

Es gibt viele Möglichkeiten für Publish/Subscribe-Messaging-Systeme. Was macht Apache Kafka zu einer guten Wahl?

Mehrere Erzeuger

Kafka ist in der Lage, nahtlos mit mehreren Produzenten umzugehen, egal ob diese Clients viele Topics oder dasselbe Topic verwenden. Das macht das System ideal, um Daten aus vielen Frontend-Systemen zu aggregieren und konsistent zu machen. Eine Website, die ihren Nutzern Inhalte über eine Reihe von Microservices zur Verfügung stellt, kann zum Beispiel ein einziges Topic für Seitenansichten haben, in das alle Services in einem gemeinsamen Format schreiben können. Verbraucheranwendungen können dann einen einzigen Stream von Seitenansichten für alle Anwendungen auf der Website erhalten, ohne dass sie den Konsum von mehreren Topics, eines für jede Anwendung, koordinieren müssen.

Mehrere Verbraucher

Zusätzlich zu mehreren Produzenten ist Kafka so konzipiert, dass mehrere Konsumenten einen einzelnen Nachrichtenstrom lesen können, ohne sich gegenseitig zu behindern. Dies steht im Gegensatz zu vielen Warteschlangensystemen, bei denen eine Nachricht, sobald sie von einem Client konsumiert wurde, für keinen anderen mehr verfügbar ist. Mehrere Kafka-Konsumenten können sich zu einer Gruppe zusammenschließen und sich einen Stream teilen, um sicherzustellen, dass die gesamte Gruppe eine bestimmte Nachricht nur einmal verarbeitet.

Festplattenbasierte Aufbewahrung

Kafka kann nicht nur mit mehreren Konsumenten umgehen, sondern die dauerhafte Speicherung von Nachrichten bedeutet, dass die Konsumenten nicht immer in Echtzeit arbeiten müssen. Die Nachrichten werden auf die Festplatte geschrieben und mit konfigurierbaren Aufbewahrungsregeln gespeichert. Diese Optionen können pro Thema ausgewählt werden, so dass verschiedene Nachrichtenströme je nach den Bedürfnissen der Verbraucher unterschiedlich lange aufbewahrt werden können. Die dauerhafte Speicherung bedeutet, dass bei einem Rückstand des Verbrauchers, sei es durch eine langsame Verarbeitung oder ein hohes Verkehrsaufkommen, keine Gefahr besteht, Daten zu verlieren. Das bedeutet auch, dass die Verbraucher gewartet werden können, indem die Anwendungen für eine kurze Zeit offline genommen werden, ohne dass die Gefahr besteht, dass Nachrichten auf dem Produzenten zurückbleiben oder verloren gehen. Die Konsumenten können angehalten werden, und die Nachrichten bleiben in Kafka erhalten. So können sie neu gestartet werden und die Verarbeitung der Nachrichten ohne Datenverlust fortsetzen, wo sie aufgehört haben.

Skalierbar

Die flexible Skalierbarkeit von Kafka macht es einfach, jede Datenmenge zu verarbeiten. Nutzer können mit einem einzelnen Broker als Proof of Concept beginnen, einen kleinen Entwicklungscluster mit drei Brokern aufbauen und mit einem größeren Cluster mit zehn oder sogar hunderten von Brokern in die Produktion gehen, der mit der Zeit wächst, wenn die Datenmenge steigt. Erweiterungen können durchgeführt werden, während der Cluster online ist, ohne dass dies Auswirkungen auf die Verfügbarkeit des gesamten Systems hat. Das bedeutet auch, dass ein Cluster mit mehreren Brokern den Ausfall eines einzelnen Brokers verkraften und die Kunden weiterhin bedienen kann. Cluster, die mehr gleichzeitige Ausfälle verkraften müssen, können mit höheren Replikationsfaktoren konfiguriert werden. Die Replikation wird in Kapitel 7 ausführlicher behandelt.

Hohe Leistung

All diese Funktionen machen Apache Kafka zu einem Publish/Subscribe-Messaging-System mit hervorragender Leistung unter hoher Last. Producer, Consumer und Broker können so skaliert werden, dass sie problemlos sehr große Nachrichtenströme verarbeiten können. Dabei bleibt die Latenzzeit von der Erzeugung einer Nachricht bis zur Bereitstellung an die Konsumenten unter einer Sekunde.

Plattform-Funktionen

Das Apache Kafka-Kernprojekt hat auch einige Streaming-Plattformfunktionen hinzugefügt, die es Entwicklern viel einfacher machen können, gängige Aufgaben zu erledigen. Es handelt sich dabei zwar nicht um vollständige Plattformen, die in der Regel eine strukturierte Laufzeitumgebung wie YARN umfassen, aber diese Funktionen sind in Form von APIs und Bibliotheken vorhanden, die eine solide Grundlage bilden und flexibel sind, wo sie ausgeführt werden können. Kafka Connect hilft bei der Aufgabe, Daten aus einem Quelldatensystem zu ziehen und in Kafka zu pushen oder Daten aus Kafka zu ziehen und in ein Sink-Datensystem zu pushen. Kafka Streams bietet eine Bibliothek für die einfache Entwicklung von Stream Processing-Anwendungen, die skalierbar und fehlertolerant sind. Connect wird in Kapitel 9 besprochen, während Streams in Kapitel 14 ausführlich behandelt wird.

Das Daten-Ökosystem

An den Umgebungen, die wir für die Datenverarbeitung aufbauen, sind viele Anwendungen beteiligt. Wir haben Inputs in Form von Anwendungen definiert, die Daten erstellen oder sie auf andere Weise in das System einbringen. Wir haben Outputs in Form von Metriken, Berichten und anderen Datenprodukten definiert. Wir erstellen Schleifen, in denen einige Komponenten Daten aus dem System lesen, sie mit Daten aus anderen Quellen umwandeln und sie dann wieder in die Dateninfrastruktur einspeisen, um sie an anderer Stelle zu verwenden. Dies geschieht für zahlreiche Datentypen, die sich alle durch ihren Inhalt, ihre Größe und ihre Verwendung unterscheiden.

Apache Kafka ist das Kreislaufsystem für das Datenökosystem, wie in Abbildung 1-9 dargestellt. Es transportiert Nachrichten zwischen den verschiedenen Mitgliedern der Infrastruktur und bietet eine einheitliche Schnittstelle für alle Clients. Wenn es mit einem System zur Bereitstellung von Nachrichtenschemata gekoppelt ist, sind Produzenten und Konsumenten nicht mehr auf eine enge Kopplung oder direkte Verbindungen angewiesen. Komponenten können hinzugefügt und entfernt werden, wenn Geschäftsfälle erstellt und aufgelöst werden, und die Produzenten müssen sich keine Gedanken darüber machen, wer die Daten nutzt oder wie viele Anwendungen sie konsumieren.

kdg2 0109
Abbildung 1-9. Ein Big-Data-Ökosystem

Anwendungsfälle

Aktivität verfolgen

Der ursprüngliche Anwendungsfall für Kafka, wie er bei LinkedIn entwickelt wurde, ist die Verfolgung von Nutzeraktivitäten. Die Nutzer einer Website interagieren mit Frontend-Anwendungen, die Nachrichten über die Aktionen des Nutzers erzeugen. Dabei kann es sich um passive Informationen handeln, wie z. B. Seitenaufrufe und Klickverfolgung, oder um komplexere Aktionen, wie z. B. Informationen, die ein Nutzer zu seinem Profil hinzufügt. Die Nachrichten werden in einem oder mehreren Themen veröffentlicht, die dann von Anwendungen im Backend genutzt werden. Diese Anwendungen können Berichte erstellen, maschinelle Lernsysteme mit Daten füttern, Suchergebnisse aktualisieren oder andere Vorgänge durchführen, die für ein umfassendes Nutzererlebnis notwendig sind.

Nachrichtenübermittlung

Kafka wird auch für das Messaging verwendet, wenn Anwendungen Benachrichtigungen (z. B. E-Mails) an Nutzerinnen und Nutzer senden müssen. Diese Anwendungen können Nachrichten erstellen, ohne sich um die Formatierung oder die Art und Weise, wie die Nachrichten tatsächlich gesendet werden, kümmern zu müssen. Eine einzige Anwendung kann dann alle zu versendenden Nachrichten lesen und sie einheitlich verarbeiten, einschließlich:

  • Formatierung der Nachrichten (auch Dekoration genannt) unter Verwendung eines gemeinsamen Look and Feel

  • Sammeln mehrerer Nachrichten in einer einzigen Benachrichtigung, die versendet wird

  • Anwendung der Präferenzen einer Nutzerin oder eines Nutzers, wie sie oder er Nachrichten erhalten möchte

Durch die Verwendung einer einzigen Anwendung wird die Notwendigkeit vermieden, Funktionen in mehreren Anwendungen zu duplizieren, und es werden Vorgänge wie die Aggregation ermöglicht, die sonst nicht möglich wären.

Metriken und Protokollierung

Kafka ist auch ideal, um Anwendungs- und Systemmetriken und Protokolle zu sammeln. Dies ist ein Anwendungsfall , bei dem die Möglichkeit, dass mehrere Anwendungen dieselbe Art von Nachricht erzeugen, besonders gut zum Tragen kommt. Die Anwendungen veröffentlichen regelmäßig Metriken in einem Kafka-Topic, die von Systemen zur Überwachung und Alarmierung genutzt werden können. Sie können auch in einem Offline-System wie Hadoop verwendet werden, um längerfristige Analysen durchzuführen, z. B. Wachstumsprognosen. Logmeldungen können auf die gleiche Weise veröffentlicht und an spezielle Log-Suchsysteme wie Elasticsearch oder Sicherheitsanalyseanwendungen weitergeleitet werden. Ein weiterer Vorteil von Kafka besteht darin, dass bei einer Änderung des Zielsystems (z. B. bei einer Aktualisierung der Log-Speicherung) weder die Frontend-Anwendungen noch die Aggregationsmittel geändert werden müssen.

Commit-Log

Da Kafka auf dem Konzept eines Commit-Logs basiert, können Änderungen an der Datenbank in Kafka veröffentlicht werden, und Anwendungen können diesen Stream einfach überwachen, um Live-Aktualisierungen zu erhalten, sobald sie geschehen. Dieser Changelog-Stream kann auch zur Replikation von Datenbankaktualisierungen auf ein entferntes System oder zur Konsolidierung von Änderungen aus mehreren Anwendungen in einer einzigen Datenbankansicht verwendet werden. Die dauerhafte Speicherung ist hier nützlich, um einen Puffer für das Changelog bereitzustellen, so dass es bei einem Ausfall der konsumierenden Anwendungen wieder abgespielt werden kann. Alternativ können log-kompaktierte Topics verwendet werden, um eine längere Aufbewahrung zu ermöglichen, indem nur eine einzige Änderung pro Schlüssel gespeichert wird.

Stream-Verarbeitung

Ein weiterer Bereich, der zahlreiche Anwendungsarten bietet, ist die Stream-Verarbeitung. Während fast alle Anwendungen von Kafka als Stream Processing bezeichnet werden können, wird der Begriff normalerweise für Anwendungen verwendet, die ähnliche Funktionen wie die Map/Rece-Verarbeitung in Hadoop bieten. Bei Hadoop werden die Daten normalerweise über einen langen Zeitraum, entweder Stunden oder Tage, gesammelt. Die Stream-Verarbeitung verarbeitet Daten in Echtzeit, d. h. so schnell wie die Nachrichten erzeugt werden. Stream-Frameworks ermöglichen es den Nutzern, kleine Anwendungen zu schreiben, die mit Kafka-Nachrichten arbeiten und Aufgaben wie das Zählen von Metriken, die Partitionierung von Nachrichten für eine effiziente Verarbeitung durch andere Anwendungen oder die Umwandlung von Nachrichten unter Verwendung von Daten aus verschiedenen Quellen übernehmen. Die Stream-Verarbeitung wird in Kapitel 14 behandelt.

Kafkas Herkunft

Kafka wurde entwickelt, um das Problem der Datenpipeline bei LinkedIn zu lösen. Es wurde entwickelt, um ein hochleistungsfähiges Nachrichtensystem bereitzustellen, das viele Arten von Daten verarbeiten kann und saubere, strukturierte Daten über Nutzeraktivitäten und Systemmetriken in Echtzeit liefert.

Daten sind die Grundlage für alles, was wir tun.

Jeff Weiner, ehemaliger CEO von LinkedIn

Das Problem von LinkedIn

Ähnlich wie in dem zu Beginn dieses Kapitels beschriebenen Beispiel verfügte LinkedIn über ein System zur Erfassung von System- und Anwendungsmetriken , das benutzerdefinierte Kollektoren und Open-Source-Tools zur internen Speicherung und Darstellung von Daten verwendete. Zusätzlich zu den traditionellen Metriken wie CPU-Auslastung und Anwendungsleistung gab es eine ausgefeilte Funktion zur Nachverfolgung von Anfragen, die das Überwachungssystem nutzte und Einblicke in die Ausbreitung einer einzelnen Benutzeranfrage durch die internen Anwendungen ermöglichte. Das Überwachungssystem wies jedoch viele Fehler auf. Dazu gehörten die Erfassung von Metriken auf Basis von Polling, große Intervalle zwischen den Metriken und keine Möglichkeit für die Anwendungseigentümer, ihre eigenen Metriken zu verwalten. Das System war sehr berührungsintensiv, da für die meisten einfachen Aufgaben ein menschliches Eingreifen erforderlich war, und es war inkonsistent, da die verschiedenen Systeme unterschiedliche Namen für dieselben Messwerte hatten.

Gleichzeitig wurde ein System zur Verfolgung der Benutzeraktivitäten entwickelt. Dabei handelte es sich um einen HTTP-Dienst, mit dem sich die Frontend-Server in regelmäßigen Abständen verbinden und einen Stapel von Nachrichten (im XML-Format) an den HTTP-Dienst übermitteln. Diese Stapel wurden dann an Offline-Verarbeitungsplattformen weitergeleitet, wo die Dateien geparst und zusammengestellt wurden. Dieses System hatte viele Fehler. Die XML-Formatierung war uneinheitlich, und das Parsen war rechenaufwändig. Um die Art der erfassten Nutzeraktivitäten zu ändern, musste ein erheblicher Teil der Arbeit zwischen den Frontends und der Offline-Verarbeitung koordiniert werden. Selbst dann brach das System ständig wegen sich ändernder Schemata zusammen. Das Tracking basierte auf einer stündlichen Stapelverarbeitung und konnte daher nicht in Echtzeit verwendet werden.

Die Überwachung und die Verfolgung von Nutzeraktivitäten konnten nicht denselben Backend-Dienst nutzen. Der Überwachungsdienst war zu klobig, das Datenformat war nicht auf die Aktivitätsverfolgung ausgerichtet und das Polling-Modell für die Überwachung war nicht mit dem Push-Modell für die Verfolgung kompatibel. Gleichzeitig war der Tracking-Dienst zu instabil, um ihn für Metriken zu nutzen, und die stapelorientierte Verarbeitung war nicht das richtige Modell für die Überwachung und Alarmierung in Echtzeit. Die Überwachungs- und Tracking-Daten wiesen jedoch viele Gemeinsamkeiten auf, und eine Korrelation der Informationen (z. B. wie sich bestimmte Arten von Nutzeraktivitäten auf die Anwendungsleistung auswirken) war äußerst wünschenswert. Ein Rückgang bestimmter Arten von Benutzeraktivitäten konnte auf Probleme mit der Anwendung hinweisen, die diese Aktivitäten bediente, aber eine stundenlange Verzögerung bei der Verarbeitung von Aktivitätsbatches bedeutete eine langsame Reaktion auf diese Art von Problemen.

Zunächst wurden bestehende Open-Source-Lösungen gründlich untersucht, um ein neues System zu finden, das einen Echtzeit-Zugriff auf die Daten ermöglicht und so skalierbar ist, dass es den erforderlichen Nachrichtenverkehr bewältigen kann. Es wurden Prototypsysteme mit ActiveMQ eingerichtet, die jedoch zu diesem Zeitpunkt nicht skalierbar waren. Außerdem war es eine anfällige Lösung für die Art und Weise, wie LinkedIn es nutzen wollte. Wir entdeckten viele Schwachstellen in ActiveMQ, die dazu führten, dass die Broker eine Pause einlegten. Diese Pausen führten dazu, dass die Verbindungen zu den Kunden unterbrochen wurden und die Anwendungen nicht mehr in der Lage waren, die Anfragen der Nutzer zu bedienen. Es wurde beschlossen, eine eigene Infrastruktur für die Datenpipeline zu entwickeln.

Die Geburt von Kafka

Das Entwicklungsteam bei LinkedIn wurde von Jay Kreps geleitet, einem leitenden Softwareentwickler, der zuvor für die Entwicklung und Open-Source-Veröffentlichung von Voldemort, einem verteilten Key-Value-Speichersystem, verantwortlich war. Zum anfänglichen Team gehörten auch Neha Narkhede und später Jun Rao. Gemeinsam machten sie sich daran, ein Nachrichtensystem zu entwickeln, das sowohl die Anforderungen des Überwachungs- als auch des Nachverfolgungssystems erfüllen und für die Zukunft skalieren konnte. Die wichtigsten Ziele waren:

  • Entkopplung von Produzenten und Konsumenten durch ein Push-Pull-Modell

  • Persistenz der Nachrichtendaten innerhalb des Nachrichtensystems, damit mehrere Verbraucher

  • Optimiere für einen hohen Durchsatz an Nachrichten

  • Ermöglicht die horizontale Skalierung des Systems, wenn die Datenströme wachsen

Das Ergebnis war ein Publish/Subscribe-Messaging-System mit einer für Messaging-Systeme typischen Schnittstelle und einer Speicherung, die eher an ein Log-Aggregationssystem erinnert. In Kombination mit dem Einsatz von Apache Avro für die Serialisierung von Nachrichten konnte Kafka sowohl Metriken als auch die Nachverfolgung von Nutzeraktivitäten in einer Größenordnung von Milliarden von Nachrichten pro Tag verarbeiten. Die Skalierbarkeit von Kafka hat dazu beigetragen, dass LinkedIn mehr als sieben Billionen Nachrichten produziert (Stand: Februar 2020) und täglich mehr als fünf Petabyte an Daten verbraucht hat.

Open Source

Kafka wurde Ende 2010 als Open-Source-Projekt auf GitHub veröffentlicht. Als es in der Open-Source-Gemeinde an Aufmerksamkeit gewann, wurde es im Juli 2011 als Inkubator-Projekt der Apache Software Foundation vorgeschlagen und angenommen. Apache Kafka verließ den Inkubator im Oktober 2012. Seitdem wird kontinuierlich an dem Projekt gearbeitet und es hat eine solide Gemeinschaft von Mitwirkenden und Committern außerhalb von LinkedIn gefunden. Kafka wird heute in einigen der größten Datenpipelines der Welt eingesetzt, darunter bei Netflix, Uber und vielen anderen Unternehmen.

Die weite Verbreitung von Kafka hat auch ein gesundes Ökosystem rund um das Kernprojekt geschaffen. In Dutzenden von Ländern auf der ganzen Welt gibt es aktive Meetup-Gruppen, in denen Stream Processing vor Ort diskutiert und unterstützt wird. Außerdem gibt es zahlreiche Open-Source-Projekte, die mit Apache Kafka zu tun haben. LinkedIn unterhält mehrere davon, darunter Cruise Control, Kafka Monitor und Burrow. Zusätzlich zu seinen kommerziellen Angeboten hat Confluent Projekte wie ksqlDB, eine Schema-Registry und einen REST-Proxy unter einer Community-Lizenz veröffentlicht (die nicht streng Open Source ist, da sie Nutzungsbeschränkungen enthält). Einige der beliebtesten Projekte sind in Anhang B aufgeführt.

Kommerzielles Engagement

Im Herbst 2014 verließen Jay Kreps, Neha Narkhede und Jun Rao LinkedIn und gründeten Confluent, ein Unternehmen, das sich auf die Entwicklung, den Unternehmenssupport und die Schulung für Apache Kafka konzentriert. Sie schlossen sich auch anderen Unternehmen (wie Heroku) an, die Cloud-Dienste für Kafka anbieten. Durch eine Partnerschaft mit Google bietet Confluent verwaltete Kafka-Cluster auf der Google Cloud Platform sowie ähnliche Dienste auf Amazon Web Services und Azure an. Eine der anderen wichtigen Initiativen von Confluent ist die Organisation der Kafka Summit Konferenzreihe. Der Kafka Summit wurde 2016 ins Leben gerufen und findet jährlich in den USA und in London statt. Er bietet der Community einen Ort, an dem sie sich auf globaler Ebene treffen und ihr Wissen über Apache Kafka und verwandte Projekte austauschen kann.

Der Name

Die Leute fragen oft, wie Kafka zu seinem Namen gekommen ist und ob er etwas Bestimmtes über die Anwendung selbst aussagt. Jay Kreps hat die folgenden Informationen gegeben:

Ich dachte, da Kafka ein für das Schreiben optimiertes System war, würde es Sinn machen, den Namen eines Schriftstellers zu verwenden. Ich hatte an der Uni viele Literaturkurse belegt und mochte Franz Kafka. Außerdem klang der Name cool für ein Open-Source-Projekt.

Im Grunde gibt es also nicht viel Beziehung.

Erste Schritte mit Kafka

Jetzt, wo wir alles über Kafka und seine Geschichte wissen, können wir es einrichten und unsere eigene Datenpipeline aufbauen. Im nächsten Kapitel werden wir uns mit der Installation und Konfiguration von Kafka beschäftigen. Außerdem geht es um die Auswahl der richtigen Hardware für den Betrieb von Kafka und um einige Dinge, die beim Übergang in den Produktionsbetrieb zu beachten sind.

Get Kafka: The Definitive Guide, 2. Auflage now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.