Im Rahmen unserer Event-Driven Systems-Reihe hat Ricky im ersten Teil über Apache Kafka als der meist eingesetze Message-Broker geschrieben und warum man Kafka einsetzen sollte. Im zweiten Teil ging darum wie man ein Apache Kafka in der Cloud ausrollen kann, dies macht man, so hat es uns Raffael gezeigt, nämlich am besten mit Strimzi. Im ersten Teil haben wir uns Apache Kafka angeschaut und wofür sich Kafka besonders eignet. Im dritten Teil welcher ich euch heute präsentieren möchte, geht es darum ob es nicht eine Cloud-native Alternative zu Kafka Apache gibt und ob man diese nicht lieber auf Ihrer Cloud ausrollen sollte, da eine Cloud-native Alternative ressourceneffizienter und somit kostengünstiger sein könnte. Die Alternative gibt es und heisst NATS. Somit nehmen wir gemeinsam einen weiteren Message-Broker unter die Lupe, welches wie etwa Apache ohne eine darunterliegende JVM auskommt und damit wesentlich leichtgewichtiger ist.
NATS Server
NATS ist ein Open-Source (Apache 2.0) und Cloud-native Messaging System für adaptive Edge- und verteilte Systeme. Es wurde ursprünglich von Derek Collison in Ruby geschrieben und später dann zu Go portiert. NATS (oder NATS Messaging) ermöglicht den Austausch zwischen Applikationen und Services. Dabei werden Daten in Nachrichten verpackt, welche dann per ‘Subject’ und nicht per IP oder DNS Name adressiert werden. Dadurch wird der darunter liegende physische Netzwerk Layer abstrahiert. Die zu sendenden Daten werden vom Sender verschlüsselt in eine Nachricht “verpackt”. Die Nachricht kann dann von einem oder mehreren Empfängern empfangen, entschlüsselt und verarbeitet werden.
Es gibt derzeit über 40 Client API’s. Unter anderem werden die folgenden Technologien und Programmiersprachen unterstützt. Go, Java, JavaScript/TypeScript, Python, Ruby, Rust, C#, C, and NGINX.
Dabei ist zu beachten, dass NATS keine Nachrichten persistiert. Ist also ein Client zum Zeitpunkt der Nachricht nicht verfügbar, so ist diese Nachricht für ihn nicht mehr sichtbar.
NATS Streaming
Aus diesem Grund gibt es NATS Streaming. NATS Streaming implementiert Nachrichten-Persistierung und eine Nachrichten-Delivery-Garantie. So muss jeder verbundene Client beim Erhalt der Nachricht ein ACK
, kurz für Acknowledge, senden. Tut er dies nicht, so wird die Nachricht nach einer bestimmten Zeit nochmals versendet. Das bedeutet, dass es sein kann, dass eine Nachricht doppelt beim Empfänger ankommt.
Ein NATS Streaming Server beinhaltet einen NATS-Server. Die Nachrichten werden vom NATS-Server weiterhin empfangen und an den Streaming-Server weitergeleitet.
Der NATS Streaming Server bietet kann zwar sicher und hochverfügbar zur Verfügung gestellt werden, allerdings gibt es einige Einschränkungen wegen der Architektur.
-
NATS Streaming ist keine ‘work queue’, es ist ‘message log based’. Dadurch werden Nachrichten nicht durch
ACK
s gelöscht, sondern nur durch Limitationen. -
Nicht horizontal skalierbar (#999)
-
Schlechte Integration mit NATS 2.0/accounts/security Konzepten. Keine Mandantenfähigkeit (#1043)
-
Clients können selbst keine Nachrichten ‘pullen’, Nachrichten werden nur zu ihnen ‘pushed’.
-
Clients können sich nicht auf spezifische Channels registrieren (#1122)
NATS Jetstream
Im März 2021 wurde das Release 2.2.0 von NATS veröffentlicht. Mit diesem Release wurde NATS Jetstream eingeführt, wodurch viele Probleme von NATS Streaming behoben werden konnten. (siehe NATS Streaming)
Ausserdem bringt Jetstream einige Features mit. Ein sehr interessantes wollen wir uns im Detail anschauen.
Wildcards
Im Kern ist NATS dafür zuständig Nachrichten zu versenden und zu empfangen. Senden und Empfangen basiert dabei auf “Subjects”, welche Nachrichten in Streams oder Topics zuordnen. Ein Subject ist ein einfacher case-sensitiver String, welche aus alphanumerischen Zeichen und dem “.” bestehen darf.
Der Punkt hat eine weitere nützliche Funktion. Man kann hiermit eine “Subject”-Hierarchie aufbauen. Beispielsweise könnte eine Struktur folgendermassen aussehen. Dabei handelt es sich um eine logische Struktur.
|
|
NATS bietet uns nun zwei Wildcards, die vom Empfänger genutzt werden können, um auf mehrere Subjects zu hören.
Matching A Single Token (*)
Will ein Empfänger alle Nachrichten von den News der intern genutzten Programmiersprachen erhalten, so kann er folgendes abonnieren.
|
|
Damit erhält er nun alle News aus der Go- und Java-Welt. Das Wildcard bedeutet jedoch nur genau ein String. Will man also alle News von Programmiersprachen und Frameworks erhalten, so kann man dieses nicht nutzen, da com.bova.developer.*
nur eine weitere Ebene selektieren (also com.bnova.developer.language oder com.bnova.developer.framework
) würde.
Matching Multiple Tokens (>)
Um nun mehrere Ebenen zu selektieren, gibt es einen weiteren Wildcard.
|
|
Damit lassen sich nun alle Subjects unter com.bnova.developer
selektieren. Es ist auch möglich alle Subjects zu abonnieren, indem man folgendes Subject nutzt. Natürlich kann man nur die lesen, auf die man auch Zugriff hat. Nur am Ende nutzbar!
|
|
Es ist auch möglich beide Wildcards zu mixen.
|
|
würde beispielsweise com.bnova
oder auch ch.bnova
selektieren.
NATS in action
Wollen wir uns nun anschauen, wie NATS in der Praxis funktioniert. Wir starten uns einen lokalen NATS Server und nutzen dafür das offizielle Docker Image. Damit wir direkt Jetstream verwenden, müssen wir als Argument noch -js
angeben.
|
|
Nun starten wir uns noch einen zweiten Container, in dem alle NATS Tools bereits installiert sind.
|
|
Wie wir sehen können erhalten wir nun einen Prompt und können nun beginnen, mit dem Server zu interagieren.
Streams
Wollen wir uns als Erstes einen Stream anlegen. Streams definieren wie Nachrichten gespeichert und aufbewahrt werden. Streams konsumieren normale NATS Subjects. Jede Nachricht, welche in diesen Subjects gefunden wird, wird an den definierten Storage gesendet.
|
|
Schauen wir uns die Konfigurationen im Detail an
|
|
-
Subjects to consume=
com.bnova.>
: Hier definieren wir auf welche Subjects wir hören wollen -
Storage Backend=
file
: Soll der Stream im File oder Speicher gehalten werden -
Retention Policy=
Limits
: Es sollen nur eine bestimmte Anzahl an Nachrichten gespeichert werden -
Discard Policy=
Old
: Alte Nachrichten sollen gelöscht werden, wenn Anzahl der max. Nachrichten erreicht wird -
Stream Messages Limit=
-1
: Anzahl Nachrichten im Stream (-1 unendlich) -
Message size limit=
-1
: Grösse der gesamten Nachrichten (-1 unendlich) -
Maximum message age limit=
2m
: Nachrichten werden für 2 Minuten aufbewahrt -
Maximum individual message size=
-1
: Maximale Grösse einer Nachricht (-1 unendlich) -
Duplicate tracking time window=
2m
: Zeit in der auf Duplikate geprüft wird -
Replicas=
1
: Anzahl der Replicas
Die erste Nachricht schreiben und lesen
Nun können wir unsere erste Nachricht in den Stream schreiben. Dazu führen wir den folgenden Befehl aus:
|
|
Und schauen uns direkt an, wie unser Stream aussieht:
|
|
Wie wir sehen können, ist nun eine Nachricht in unserem Stream vorhanden. Diese können wir nun 2 Minuten lang abrufen, bevor diese durch die Konfiguration Maximum Age wieder gelöscht wird.
Wollen wir uns nun einen Consumer schreiben, mit dem wir die Nachricht aus dem Stream wieder lesen können.
|
|
Schauen wir uns auch hier die Konfiguration wieder im Detail an:
|
|
-
Consumer name=
language-consumer
: Name des Consumers -
Delivery target=
pull
: Consumer pullt die Nachrichten beim Sender -
Start policy=
all
:Alle Nachrichten im Stream sollen gelesen werden -
Replay policy=
instant
: Consumer wird alle Nachrichten so schnell wie möglich erhalten -
Filter Stream by subject=
com.bnova.language
: Nur aus diesem Subject lesen -
Maximum Allowed Deliveries=
-1
: ??? -
Maximum Acknowledgements Pending=
0
: ???
Nun können wir mit unserem neu angelegten Consumer die Nachricht aus dem Stream lesen. Dazu müssen wir folgenden Befehl eingeben:
|
|
So, jetzt wissen Sie was NATS ist und wie man eine NATS-Umgebung in Ihren Kubernetes-Cluster einrichten kann. Stay tuned!