NATS Logo by Example

Request-Reply in Messaging

The request-reply pattern allows a client to send a message and expect a reply of some kind. In practice, the request message will either be a command, which is an intention for service to carry out some work that results in a state change, or a query, which is a request for information.

Unlike request-reply constrained protocols like HTTP, NATS is not limited to a strict point-to-point interaction between a client and server. The request-reply pattern is built on top of the core publish-subscribe model.

By default, this means that any one of subscribers could be a responder and reply to the client. However, because NATS is not limited to point-to-point interactions, the client could indicate to NATS that multiple replies should be allowed.

This example shows the basics of the request-reply pattern including the standard “no responders” error if there are no subscribers available to handle and reply to the requesting message.

CLI Go Python JavaScript Rust C# C#2 Java Ruby Elixir Crystal C
Jump to the output or the recording
$ nbe run messaging/request-reply/java
View the source code or learn how to run this example yourself

Code

package example;


import io.nats.client.Connection;
import io.nats.client.Dispatcher;
import io.nats.client.Message;
import io.nats.client.Nats;


import java.io.IOException;
import java.time.Duration;
import java.util.concurrent.*;


public class Main {
  public static void main(String[] args) {
    String natsURL = System.getenv("NATS_URL");
    if (natsURL == null) {
      natsURL = "nats://127.0.0.1:4222";
    }

Initialize a connection to the server. The connection is AutoCloseable on exit.

    try (Connection nc = Nats.connect(natsURL)) {

Reply

Create a message dispatcher for handling messages in a separate thread and then subscribe to the target subject which leverages a wildcard greet.*. When a user makes a “request”, the client populates the reply-to field and then listens (subscribes) to that as a subject. The replier simply publishes a message to that reply-to.

      Dispatcher dispatcher = nc.createDispatcher((msg) -> {
        String name = msg.getSubject().substring(6);
        String response = "hello " + name;
        nc.publish(msg.getReplyTo(), response.getBytes());
      });
      dispatcher.subscribe("greet.*");

Request

Make a request and wait a most 1 second for a response.

      Message m = nc.request("greet.bob", null, Duration.ofSeconds(1));
      System.out.println("Response received: " + new String(m.getData()));

A request can also be made asynchronously.

      try {
        CompletableFuture<Message> future = nc.request("greet.pam", null);
        m = future.get(1, TimeUnit.SECONDS);
        System.out.println("Response received: " + new String(m.getData()));
      } catch (ExecutionException e) {
        System.out.println("Something went wrong with the execution of the request: " + e);
      } catch (TimeoutException e) {
        System.out.println("We didn't get a response in time.");
      } catch (CancellationException e) {
        System.out.println("The request was cancelled due to no responders.");
      }

Once we unsubscribe there will be no subscriptions to reply.

      dispatcher.unsubscribe("greet.*");

If there are no-responders to a synchronous request we just time out and get a null response.

      m = nc.request("greet.fred", null, Duration.ofMillis(300));
      System.out.println("Response was null? " + (m == null));

If there are no-responders to an asynchronous request we get a cancellation exception.

      try {
        CompletableFuture<Message> future = nc.request("greet.sue", null);
        m = future.get(1, TimeUnit.SECONDS);
        System.out.println("Response received: " + new String(m.getData()));
      } catch (ExecutionException e) {
        System.out.println("Something went wrong with the execution of the request: " + e);
      } catch (TimeoutException e) {
        System.out.println("We didn't get a response in time.");
      } catch (CancellationException e) {
        System.out.println("The request was cancelled due to no responders.");
      }


    } catch (InterruptedException | IOException e) {
      e.printStackTrace();
    }
  }
}

Output

Response received: hello bob
Response received: hello pam
Response was null? true
The request was cancelled due to no responders.

Recording

Note, playback is half speed to make it a bit easier to follow.