Veröffentlichen und Empfangen von Nachrichten mit Nats Java Client

Veröffentlichen und Empfangen von Nachrichten mit Nats Java Client

1. Überblick

In diesem Tutorial verwenden wirJava Client for NATs, um eine Verbindung zuNATS Server herzustellen und Nachrichten zu veröffentlichen und zu empfangen.

NATS bietet drei Hauptmodi für den Nachrichtenaustausch. Publish/Subscribe semantics delivers messages to all subscribers of a topic. Request/Reply messaging sends requests via topics and routes responses back to the requestor.

Abonnenten können auch Nachrichtenwarteschlangengruppen beitreten, wenn sie ein Thema abonnieren. An das zugeordnete Thema gesendete Nachrichten werden nur an einen Abonnenten in der Warteschlangengruppe übermittelt.

2. Konfiguration

2.1. Maven-Abhängigkeit

Zuerst müssen wir die NATS-Bibliothek zu unserenpom.xml: hinzufügen


    io.nats
    jnats
    1.0

Die neueste Version der Bibliothekcan be found here und des Github-Projekts isthere.

2.2. NATS Server

Zweitens benötigen wir einen NATS-Server für den Nachrichtenaustausch. Es gibt Anweisungen für alle wichtigen Plattformenhere.

Wir gehen davon aus, dass auf localhost ein Server ausgeführt wird: 4222.

3. Nachrichten verbinden und austauschen

3.1. Stellen Sie eine Verbindung zu NATS her

Dieconnect()-Methode in der statischen NATS-Klasse erstelltConnections.

Wenn wir eine Verbindung mit Standardoptionen verwenden und localhost auf Port 4222 überwachen möchten, können wir die Standardmethode verwenden:

Connection natsConnection = Nats.connect();

Connectionshaben jedoch viele konfigurierbare Optionen, von denen einige überschrieben werden sollen.

Wir erstellen einOptions-Objekt und übergeben es anNats:

private Connection initConnection() {
    Options options = new Options.Builder()
      .errorCb(ex -> log.error("Connection Exception: ", ex))
      .disconnectedCb(event -> log.error("Channel disconnected: {}", event.getConnection()))
      .reconnectedCb(event -> log.error("Reconnected to server: {}", event.getConnection()))
      .build();

    return Nats.connect(uri, options);
}

NATS Connections are durable. Die API versucht, eine verlorene Verbindung wieder herzustellen.

Wir haben Rückrufe installiert, um uns darüber zu informieren, wann eine Trennung auftritt und wann die Verbindung wiederhergestellt wird. In diesem Beispiel verwenden wir Lambdas. Für Anwendungen, die mehr als nur das Ereignis protokollieren müssen, können wir Objekte installieren, die die erforderlichen Schnittstellen implementieren.

Wir können einen kurzen Test durchführen. Stellen Sie eine Verbindung her und fügen Sie einen Ruhezustand für 60 Sekunden hinzu, um den Prozess am Laufen zu halten:

Connection natsConnection = initConnection();
Thread.sleep(60000);

Führen Sie das aus. Dann stoppen und starten Sie Ihren NATS-Server:

[jnats-callbacks] ERROR com.example.nats.NatsClient
  - Channel disconnected: [email protected]
[reconnect] WARN io.nats.client.ConnectionImpl
  - couldn't connect to nats://localhost:4222 (nats: connection read error)
[jnats-callbacks] ERROR com.example.nats.NatsClient
  - Reconnected to server: [email protected]

Wir können sehen, dass die Rückrufe das Trennen und Wiederherstellen der Verbindung protokollieren.

3.2. Nachrichten abonnieren

Jetzt, da wir eine Verbindung haben, können wir an der Nachrichtenverarbeitung arbeiten.

Ein NATSMessage ist ein Container für ein Array vonbytes[]. Zusätzlich zu den erwarteten MethodensetData(byte[]) undbyte[] getData()gibt es Methoden zum Festlegen und Abrufen des Nachrichtenziels und zum Antworten auf Themen.

Wir abonnieren Themen, dieStrings. sind

NATS unterstützt sowohl synchrone als auch asynchrone Abonnements.

Schauen wir uns ein asynchrones Abonnement an:

AsyncSubscription subscription = natsConnection
  .subscribe( topic, msg -> log.info("Received message on {}", msg.getSubject()));

Die API liefertMessages an unsereMessageHandler(), in ihrem Thread.

Einige Anwendungen möchten möglicherweise den Thread steuern, der stattdessen Nachrichten verarbeitet:

SyncSubscription subscription = natsConnection.subscribeSync("foo.bar");
Message message = subscription.nextMessage(1000);

SyncSubscription hat eine blockierendenextMessage()-Methode, die für die angegebene Anzahl von Millisekunden blockiert. Wir verwenden synchrone Abonnements für unsere Tests, um die Testfälle einfach zu halten.

AsyncSubscription undSyncSubscription haben beide eineunsubscribe()-Methode, mit der wir das Abonnement schließen können.

subscription.unsubscribe();

3.3. Nachrichten veröffentlichen

Das Veröffentlichen vonMessages kann auf verschiedene Arten erfolgen.

Die einfachste Methode erfordert nur ein ThemaString und die Nachrichtbytes:

natsConnection.publish("foo.bar", "Hi there!".getBytes());

Wenn ein Herausgeber eine Antwort wünscht oder bestimmte Informationen zur Quelle einer Nachricht bereitstellen möchte, kann er auch eine Nachricht mit einem Antwortthema senden:

natsConnection.publish("foo.bar", "bar.foo", "Hi there!".getBytes());

Es gibt auch Überladungen für einige andere Kombinationen, z. B. das Übergeben vonMessage anstelle vonbytes.

3.4. Ein einfacher Nachrichtenaustausch

Bei einem gültigenConnection können wir einen Test schreiben, der den Nachrichtenaustausch überprüft:

SyncSubscription fooSubscription = natsConnection.subscribe("foo.bar");
SyncSubscription barSubscription = natsConnection.subscribe("bar.foo");
natsConnection.publish("foo.bar", "bar.foo", "hello there".getBytes());

Message message = fooSubscription.nextMessage();
assertNotNull("No message!", message);
assertEquals("hello there", new String(message.getData()));

natsConnection
  .publish(message.getReplyTo(), message.getSubject(), "hello back".getBytes());

message = barSubscription.nextMessage();
assertNotNull("No message!", message);
assertEquals("hello back", new String(message.getData()));

Wir beginnen mit dem Abonnieren von zwei Themen mit synchronen Abonnements, da diese in einem JUnit-Test viel besser funktionieren. Dann senden wir eine Nachricht an einen von ihnen und geben den anderen alsreplyTo-Adresse an.

Nachdem wir die Nachricht vom ersten Ziel gelesen haben, drehen wir die Themen um, um eine Antwort zu senden.

3.5. Wildcard-Abonnements

NATS-Server unterstützt Themenplatzhalter.

Platzhalter werden für Topic-Token verwendet, die durch das Zeichen "." Getrennt sind. Das Sternchen "*" entspricht einem einzelnen Token. Das Größer-als-Symbol ">" ist eine Platzhalterübereinstimmung für den Rest eines Themas, bei dem es sich möglicherweise um mehr als ein Token handelt.

Zum Beispiel:

  • foo. * entspricht foo.bar, foo.requests,but not foo.bar.requests

  • foo.> stimmt mit foo.bar, foo.requests, foo.bar.requests, foo.bar.example usw. überein.

Versuchen wir ein paar Tests:

SyncSubscription fooSubscription = client.subscribeSync("foo.*");

client.publishMessage("foo.bar", "bar.foo", "hello there");

Message message = fooSubscription.nextMessage(200);
assertNotNull("No message!", message);
assertEquals("hello there", new String(message.getData()));

client.publishMessage("foo.bar.plop", "bar.foo", "hello there");
message = fooSubscription.nextMessage(200);
assertNull("Got message!", message);

SyncSubscription barSubscription = client.subscribeSync("foo.>");

client.publishMessage("foo.bar.plop", "bar.foo", "hello there");

message = barSubscription.nextMessage(200);
assertNotNull("No message!", message);
assertEquals("hello there", new String(message.getData()));

4. Request/Reply Messaging

Unser Nachrichtenaustauschtest ähnelte einer allgemeinen Redewendung bei Pub- / Sub-Nachrichtensystemen. Anfrage / Antwort. NATS has explicit support for this request/reply messaging.

Publisher können einen Handler für Anforderungen mithilfe der oben beschriebenen asynchronen Abonnementmethode installieren:

AsyncSubscription subscription = natsConnection
  .subscribe("foo.bar.requests", new MessageHandler() {
    @Override
    public void onMessage(Message msg) {
        natsConnection.publish(message.getReplyTo(), reply.getBytes());
    }
});

Oder sie können auf Anfragen bei ihrem Eintreffen antworten.

Die API bietet einerequest()-Methode:

Message reply = natsConnection.request("foo.bar.requests", request.getBytes(), 100);

Diese Methode erstellt ein temporäres Postfach für die Antwort und schreibt die Antwortadresse für uns.

Request() gibt die Antwort zurück odernull, wenn die Anforderung abgelaufen ist. Das letzte Argument ist die Anzahl der Millisekunden, die gewartet werden muss.

Wir können unseren Test für Anfrage / Antwort ändern:

natsConnection.subscribe(salary.requests", message -> {
    natsConnection.publish(message.getReplyTo(), "denied!".getBytes());
});
Message reply = natsConnection.request("salary.requests", "I need a raise.", 100);
assertNotNull("No message!", reply);
assertEquals("denied!", new String(reply.getData()));

5. Nachrichtenwarteschlangen

Abonnenten können Warteschlangengruppen zum Zeitpunkt des Abonnements angeben. When a message is published to the group NATS will deliver it to a one-and-only-one subscriber.

Queue groups do not persist messages. Wenn keine Listener verfügbar sind, wird die Nachricht verworfen.

5.1. Warteschlangen abonnieren

Abonnenten geben einen Warteschlangengruppennamen alsString: an

SyncSubscription subscription = natsConnection.subscribe("topic", "queue name");

Es gibt natürlich auch eine asynchrone Version:

SyncSubscription subscription = natsConnection
  .subscribe("topic", "queue name", new MessageHandler() {
    @Override
    public void onMessage(Message msg) {
        log.info("Received message on {}", msg.getSubject());
    }
});

Das Abonnement erstellt die Warteschlange auf dem NATS-Server.

5.2. Veröffentlichen in Warteschlangen

Das Veröffentlichen von Nachrichten in Warteschlangengruppen erfordert lediglich das Veröffentlichen in dem zugeordneten Thema:

natsConnection.publish("foo",  "queue message".getBytes());

Der NATS-Server leitet die Nachricht an die Warteschlange weiter und wählt einen Nachrichtenempfänger aus.

Dies können wir mit einem Test überprüfen:

SyncSubscription queue1 = natsConnection.subscribe("foo", "queue name");
SyncSubscription queue2 = natsConnection.subscribe("foo", "queue name");

natsConnection.publish("foo", "foobar".getBytes());

List messages = new ArrayList<>();

Message message = queue1.nextMessage(200);
if (message != null) messages.add(message);

message = queue2.nextMessage(200);
if (message != null) messages.add(message);

assertEquals(1, messages.size());

Wir erhalten nur eine Nachricht.

Wenn wir die ersten beiden Zeilen in ein normales Abonnement ändern:

SyncSubscription queue1 = natsConnection.subscribe("foo");
SyncSubscription queue2 = natsConnection.subscribe("foo");

Der Test schlägt fehl, da die Nachricht beiden Teilnehmern zugestellt wird.

6. Fazit

In dieser kurzen Einführung haben wir eine Verbindung zu einem NATS-Server hergestellt und sowohl Pub / Sub-Nachrichten als auch Nachrichten in der Warteschlange mit Lastenausgleich gesendet. Wir haben uns die NATS-Unterstützung für Platzhalterabonnements angesehen. Wir haben auch das Anforderungs- / Antwort-Messaging verwendet.

Codebeispiele finden sich wie immer inover on GitHub.