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/dotnet
View the source code or learn how to run this example yourself

Code

using System;
using System.Text;
using System.Threading.Tasks;
using NATS.Client;


string natsUrl = Environment.GetEnvironmentVariable("NATS_URL");
if (natsUrl == null)
{
    natsUrl = "nats://127.0.0.1:4222";
}

Create a new connection factory to create a connection.

Options opts = ConnectionFactory.GetDefaultOptions();
opts.Url = natsUrl;

Creates a connection to nats server at the natsUrl An IConnection is IDisposable so it can be used within a using statement.

ConnectionFactory cf = new ConnectionFactory();
IConnection c = cf.CreateConnection(opts);

Reply

Create a message event handler 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.

EventHandler<MsgHandlerEventArgs> handler = (sender, args) =>
{
    string name = args.Message.Subject.Substring(6);
    string response = $"hello {name}";
    c.Publish(args.Message.Reply, Encoding.UTF8.GetBytes(response));
};
IAsyncSubscription sub = c.SubscribeAsync("greet.*", handler);

Request

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

try
{
    Msg m0 = c.Request("greet.bob", null, 1000);
    Console.WriteLine("Response received: " + Encoding.UTF8.GetString(m0.Data));
}
catch (NATSTimeoutException)
{
    Console.WriteLine($"NATSTimeoutException: The request did not complete in time.");
}

A request can also be made asynchronously

Task<Msg> task1 = c.RequestAsync("greet.pam", null);
task1.Wait(1000);
Msg m1 = task1.Result;
Console.WriteLine("Response received: " + Encoding.UTF8.GetString(m1.Data));

Once we unsubscribe there will be no subscriptions to reply.

sub.Unsubscribe();

If there are no-responders to a synchronous request we get a NATSNoRespondersException.

try
{
    c.Request("greet.fred", null, 1000);
}
catch (NATSNoRespondersException)
{
    Console.WriteLine($"NATSNoRespondersException: There were no responders listening for the subject.");
}
catch (NATSTimeoutException)
{
    Console.WriteLine($"NATSTimeoutException: The request did not complete in time.");
}

If there are no-responders to an asynchronous request we get a NATSNoRespondersException wrapped inside the AggregateException

try
{
    Task<Msg> task2 = c.RequestAsync("greet.sue", null);
    task2.Wait(1000);
}
catch (AggregateException ae)
{
    if (ae.InnerExceptions[0] is NATSNoRespondersException)
    {
        Console.WriteLine($"NATSNoRespondersException: There were no responders listening for the subject.");
    }
    else
    {
        Console.WriteLine($"Some exception: " + ae.Message);
    }
}

Output

Response received: hello bob
Response received: hello pam
NATSNoRespondersException: There were no responders listening for the subject.
NATSNoRespondersException: There were no responders listening for the subject.

Recording

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