Erste Schritte mit Apache Kafka

Bevor wir uns in all die Details stürzen, lass uns gemeinsam unsere Apache Kafka Rakete starten und einen Blick darauf werfen, wie sie sich in einem ersten Testflug bewährt. Jede Raketenwissenschaft muss schließlich irgendwann einmal beginnen.
In diesem Sinne: 3, 2, 1 … Zündung

5.11.2021
  • Erste Schritte mit Apache Kafka

    Anatoly Zelenin

    IT-Trainer und Apache Kafka Experte

  • Erste Schritte mit Apache Kafka

    Alexander Kropp

    Dozent und Trainer im Cloud-Umfeld

Buch-Cover

Dies ist ein Auszug aus unserem Buch Apache Kafka: Von den Grundlagen bis zum Produktiveinsatz. Erschienen am 12.11.2021. Erhältlich direkt bei uns, beim Hanser Verlag, Amazon, und im Buchhandel.

Dieser Blog-Artikel startet sofort mit dem Thema. Es lohnt sich vorher folgende Artikel zu lesen:

Bevor wir ergründen, warum die Kafka-Rakete überhaupt fliegen kann und wie sie es tut, zünden wir gemeinsam die Triebwerke und machen einen kleinen Testflug:

Unser Testflug mit der Apache Kafka Rakete

Topics erstellen

Wir möchten hierzu in Kafka alle Flugphasen erfassen und erstellen dazu als erstes ein Topic. Topics sind insofern ähnlich zu Tabellen in Datenbanken, als dass wir in Topics eine Ansammlung von Daten zu einem bestimmten Thema abspeichern. In unserem Fall sind es Flugdaten, daher nennen wir das Topic entsprechend flugdaten:

$ kafka-topics.sh \
	--create \
	--topic flugdaten \
	--partitions 1 \
    --replication-factor 1 \    --bootstrap-server
localhost:9092
Created topic flugdaten....

Mit dem Befehl kafka-topics.sh verwalten wir unsere Topics in Kafka. Hier weisen wir Kafka mit dem Argument --create an, das Topic flugdaten (--topic flugdaten) zu erstellen. Zunächst starten wir mit einer Partition (--partitions 1) und ohne die Daten zu replizieren (--replication-factor 1). Als Letztes geben wir an zu welchem Kafka-Cluster sich kafka-topics.sh verbinden soll. In unserem Fall nutzen wir unser lokales Cluster, welches standardmäßig auf dem Port 9092 hört (--bootstrap-server localhost:9092). Der Befehl bestätigt uns die erfolgreiche Erstellung des Topics. Sollten wir hier Fehler bekommen, liegt dies oft daran, dass Kafka noch nicht gestartet ist und somit nicht erreichbar ist, oder daran, dass das Topic schon existiert.

Nachrichten produzieren

Wir haben jetzt also einen Ort, an dem wir unsere Daten abspeichern können. Der Boardcomputer der Rakete schickt uns kontinuierlich Aktualisierungen zum Flugzustand der Rakete. Für unsere Simulation nutzen wir dafür das Kommandozeilenwerkzeug kafka-console-producer.sh. Dieser Producer und auch andere nützliche Werkzeuge werden mit Kafka direkt ausgeliefert. Der Producer verbindet sich zu Kafka, nimmt Daten von der Kommandozeile entgegen und schickt sie als Nachrichten in ein Topic (konfigurierbar über den Parameter --topic). Schreiben wir die Nachricht Countdown gestartet in unser eben erstelltes Topic flugdaten:

$ echo "Countdown gestartet" | kafka-console-producer.sh \
--topic flugdaten \
--bootstrap-server=localhost:9092...

Nachrichten konsumieren

Unsere Bodenstation möchte diese Daten jetzt lesen und zum Beispiel auf einem großen Bildschirm ausgeben, damit wir sehen, ob die Rakete wirklich so funktioniert, wie wir das von ihr erwarten. Schauen wir uns also an, was bisher passiert ist. Um unsere eben gesendete Nachricht wieder zu lesen, starten wir den kafka-console-consumer.sh, welcher ebenfalls Teil der Kafka-Familie ist:

$ timeout 10 kafka-console-consumer.sh \
--topic flugdaten \
--bootstrap-server=localhost:9092
Processed a total of 0 messages...

Wenn wir den kafka-console-consumer.sh starten, läuft er standardmäßig weiter, bis wir ihn aktiv (zum Beispiel mit STRG+C) abbrechen. Das wäre auch das gewünschte Verhalten, wenn wir den aktuellen Zustand der Rakete wirklich irgendwo anzeigen wöllten. In unserem Beispiel nutzen wir den Befehl timeout, damit der Consumer automatisch nach spätestens 10 Sekunden abbricht. Bei dem Consumer müssen wir wieder angeben, welches Topic dieser benutzen soll (--topic flugdaten).

Etwas überraschend wird keine Nachricht angezeigt. Dies liegt daran, dass der kafka-console-consumer.sh standardmäßig am Ende des Topics zu lesen beginnt und nur neue Nachrichten ausgibt. Um auch bereits geschriebene Daten anzuzeigen, müssen wir das Flag --from-beginning benutzen:

$ timeout 10 kafka-console-consumer.sh \
--topic flugdaten \
--from-beginning \
--bootstrap-server=localhost:9092
Countdown gestartet
Processed a total of 1 messages...

Was ist passiert?

Diesmal sehen wir die Nachricht Countdown gestartet! Was ist also passiert? Mit dem Befehl kafka-topics.sh haben wir das Topic flugdaten in Kafka erstellt und mit dem kafka-console-producer.sh die Nachricht Countdown gestartet produziert. Diese Nachricht haben wir dann mit dem kafka-console-consumer.sh wieder gelesen. Diesen Datenfluss können wir folgendermaßen darstellen:

In unserem Beispiel produzieren wir Daten mit dem kafka-console-producer in das Topic flugdaten und mit dem kafka-console-consumer können wir diese Daten wieder lesen.

Ohne weitere Angaben fängt der kafka-console-consumer.sh immer am Ende an zu lesen. Das heißt, wenn wir alle Nachrichten lesen möchten, müssen wir das Flag --from-beginning benutzen.

Interessanterweise, beziehungsweise anders als in vielen Messaging-Systemen, können wir Nachrichten nicht nur einmal lesen, sondern beliebig oft. Dies können wir nutzen, um zum Beispiel mehrere unabhängige Bodenstationen mit dem Topic zu verbinden, sodass alle die gleichen Daten lesen können. Oder es gibt unterschiedliche Systeme, die alle die gleichen Daten benötigen. Wir können uns vorstellen, dass wir nicht nur einen Anzeigebildschirm haben, sondern zusätzlich andere Services, wie zum Beispiel einen Service, der die Flugdaten mit aktuellen Wetterdaten abgleicht und entscheidet, ob etwas unternommen werden muss. Vielleicht möchten wir aber auch nach dem Flug die Daten analysieren und benötigen dafür die historischen Flugdaten. Dazu können wir den Consumer einfach mehrmals ausführen und bekommen jedes Mal das gleiche Ergebnis.

Wir möchten jetzt aber gerne den aktuellen Zustand der Rakete in unserem Kontrollzentrum anzeigen, und zwar so, dass sich die Anzeige sofort aktualisiert, wenn es neue Daten gibt. Dazu starten wir den kafka-console-consumer.sh (ohne Timeout) in einem weiteren Terminal-Fenster. Sobald neue Daten verfügbar sind, holt sich der Consumer diese von Kafka und zeigt sie auf der Kommandozeile an:

# Nicht vergessen: STRG+C nutzen, um den Consumer zu stoppen
$ kafka-console-consumer.sh \
	--topic flugdaten \
	--bootstrap-server=localhost:9092...

Nachrichten gleichzeitig produzieren und konsumieren

Um den Producer auf Raketen-Seite zu simulieren, starten wir jetzt den kafka-console-producer.sh. Der Befehl stoppt so lange nicht, bis wir STRG+D drücken und damit das EOF-Signal an den Producer senden:

# Nicht vergessen: STRG+D nutzen, um den Producer zu stoppen
$ kafka-console-producer.sh \
--topic flugdaten \
--bootstrap-server=localhost:9092...

Der kafka-console-producer.sh schickt pro Zeile, die wir schreiben, eine Nachricht an Kafka. Das heißt, wir können nun Nachrichten in das Terminal mit dem Producer eintippen:

# Producer Fenster:
> Countdown beendet
> Liftoff
> Atmosphäre verlassen
> Vorbereitung Wiedereintritt
> Wiedereintritt erfolgreich
> Landung erfolgreich...

Wir sollten diese auch zeitnah im Fenster mit dem Consumer sehen:

# Consumer Fenster:
Countdown beendet
Liftoff
Atmosphäre verlassen
Vorbereitung Wiedereintritt
Wiedereintritt erfolgreich
Landung erfolgreich...

Stellen wir uns vor, dass ein Teil unserer Bodencrew im Home-Office ist und den Flug von zu Hause aus verfolgen möchte. Dazu starten sie unabhängig voneinander ihren Consumer. Wir können das simulieren, indem wir in einem weiteren Terminalfenster einen kafka-console-consumer.sh starten, der alle Daten von Beginn an anzeigt:

# Fenster Consumer 2
$ kafka-console-consumer.sh \
--topic flugdaten \
--from-beginning \
--bootstrap-server=localhost:9092
Countdown gestartet
Countdown beendet
Liftoff
Atmosphäre verlassen
Vorbereitung Wiedereintritt
Wiedereintritt erfolgreich
Landung erfolgreich...

Wir sehen hier, dass Daten, die einmal geschrieben werden, parallel von mehreren Consumern gelesen werden können, ohne dass die Consumer miteinander reden müssen oder die Consumer sich vorher bei Kafka registrieren müssen. Kafka löscht erst einmal keine Daten, das heißt, dass wir auch später noch einen Consumer starten können, der historische Daten lesen kann.

Sagen wir nun, dass wir nicht nur eine Rakete gleichzeitig fliegen lassen möchten, sondern mehrere. Kafka hat damit kein Problem und kann ohne weiteres Daten von vielen Producern gleichzeitig verarbeiten. Starten wir also einen weiteren Producer in einem weiteren Terminalfenster:

# Producer für Rakete 2
$ kafka-console-producer.sh \
--topic flugdaten \
--bootstrap-server=localhost:9092
> Countdown gestartet...

Wir sehen alle Nachrichten von allen Producern in allen unseren Consumern in der Reihenfolge, in der die Nachrichten produziert wurden, auftauchen:

# Fenster Consumer 1
[…]
Landung erfolgreich
Countdown gestartet
# Fenster Consumer 2
[…]
Landung erfolgreich
Countdown gestartet...

Nun ergibt sich aber das Problem, dass wir die Nachrichten von Rakete 1 und 2 nicht voneinander unterscheiden können. Wir könnten eventuell in jede Nachricht schreiben, von welcher Rakete die Nachricht geschickt wurde. Dazu aber später mehr.

Bevor wir weitermachen sollten wir noch den Flug unserer zweiten Rakete abbrechen, da wir sie erst später starten lassen wollen:

# Producer für Rakete 2
[…]
> Countdown abgebrochen...

Zusammengefasst

Wir haben jetzt erfolgreich eine Rakete testweise gestartet und wieder gelandet. Dabei haben wir einige Daten in Kafka geschrieben. Dazu haben wir zuerst ein Topic flugdaten mit dem Kommandozeilenwerkzeug kafka-topics.sh erstellt, in welches wir alle Flugdaten für unsere Rakete schreiben. In dieses Topic haben wir mithilfe von kafka-console-producer.sh einige Daten produziert. In unserem Fall waren dies Informationen über den aktuellen Status der Rakete. Diese Daten konnten wir mithilfe des kafka-console-consumer.sh lesen und anzeigen. Wir sind sogar weiter gegangen und haben parallel mit mehreren Producern Daten produziert und mit mehreren Consumern Daten gleichzeitig gelesen. Mit dem Flag --from-beginning im kafka-console-consumer.sh waren wir in der Lage, auf historische Daten zuzugreifen. Wir haben so schon drei Kommandozeilenwerkzeuge kennengelernt, die mit Kafka ausgeliefert werden.

Nachdem wir diese Erfahrungen gesammelt haben, können wir nun alle geöffneten Terminals schließen. Producer beenden wir mit STRG+D und Consumer mit STRG+C. Dieses Beispiel soll nicht darüber hinwegtäuschen, dass Kafka überall da eingesetzt wird, wo größere Datenmengen verarbeitet werden. Aus unserer Schulungserfahrung wissen wir, dass Kafka bei vielen Automobilherstellern, Supermarktketten, Logistikdienstleistern und selbst in vielen Banken und Versicherungen intensiv eingesetzt wird.

In diesem Blog-Post haben wir einen ersten Überblick über Kafka erhalten und ein einfaches Testflug-Szenario durchexerziert. In unserem Buch setzen wir an dieser Stelle fort, indem wir tiefer in die Materie einsteigen und die Kafka-Architektur näher untersuchen. Wir werden uns anschauen, wie Kafka-Nachrichten strukturiert sind und wie genau sie in Topics organisiert werden. In diesem Zusammenhang werden wir uns auch mit der Skalierbarkeit und der Zuverlässigkeit von Kafka beschäftigen. Außerdem werden wir mehr über Producer, Consumer und das Kafka-Cluster selbst erfahren.

Das Buch ist direkt bei uns, beim Hanser-Verlag, Amazon, und überall wo es Bücher gibt erhältlich. Für Unterstützung und Fragen rund um Kafka schreib uns einfach oder buche dir ein Kennenlerngespräch hier.