Table of Contents

Advanced Options

For more advanced configuration, you can use the NatsOpts class to configure the connection to the NATS server.

For example, you can hook your logger to NatsClient to make sure all is working as expected or to get help diagnosing any issues you might have:

(For this example, you need to add Microsoft.Extensions.Logging.Console from Nuget.)

using ILoggerFactory loggerFactory = LoggerFactory.Create(configure: builder => builder.AddConsole());

NatsOpts opts = new NatsOpts { LoggerFactory = loggerFactory };

await using NatsClient nc = new NatsClient(opts);

NatsClient vs NatsConnection

NatsClient is a high-level API that wraps NatsConnection and provides a more user-friendly interface to interact with the NATS server. It is the recommended way to interact with the NATS server for beginners. However, if you need to access the underlying NatsConnection instance, you can do so by using the Connection property of NatsClient or by creating a new instance of NatsConnection.

So, What's the Difference?

NatsClient implements INatsClient and provides a high-level APIs, and also sets up the serializers to use the expected formats for message types like int, string, byte[], and data classes for ad hoc JSON serialization.

NatsConnection is the underlying class that manages the connection to the NATS server. It provides more advanced APIs and allows you to configure the connection in more detail. NatsConnectionimplements INatsConnection which extends INatsClient with advanced APIs, so you can use it as a NatsClient instance without any changes to your code. When you instantiate a NatsConnection with default options, you would only have basic serialization for int, string, and byte[] types, and you would need to set up the serializers for your data classes if you want to use e.g., JSON serialization.

The other difference is that NatsClient sets SubPendingChannelFullMode internal channel option to BoundedChannelFullMode.Wait to avoid dropping messages when the subscriber's internal channel is full. This is a good default for most cases, but you can change it by setting the SubPendingChannelFullMode option in NatsClient constructor.


NatsOpts opts = new NatsOpts
{
    // You need to set pending in the constructor and not use
    // the option here, as it will be ignored.
    SubPendingChannelFullMode = BoundedChannelFullMode.DropOldest,

    // Your custom options
    SerializerRegistry = new MyProtoBufSerializerRegistry(),

    // ...
};

await using NatsClient nc = new NatsClient(opts, pending: BoundedChannelFullMode.DropNewest);

You can also use the NatsConnection class directly.


NatsOpts opts = new NatsOpts
{
    // Your custom options
};

await using NatsConnection nc = new NatsConnection(opts);

Which One Should I Use?

If you are new to NATS, you should use NatsClient as it provides a more user-friendly interface with sensible defaults especially for serialization. If you need more control over the connection options, AOT deployments, or custom serializers, you should use NatsConnection.

See also serialization for more information on how to set up custom serializers.

Note

Every NatsClient (and the underlying NatsConnection) instance is a TCP connection to a NATS server. Typically an application will only need one connection, and many subscriptions and publishers would share that same connection. Connections are relatively heavyweight and expensive to create while subscriptions and publishers are lightweight internal NATS protocol handlers. NATS.Net should be able to handle large numbers of subscriptions and publishers per connection.

Subscriptions with Lower Level Control

The SubscribeAsync() method is a convenient way to subscribe to a subject and receive messages without much effort. If you need more control over how subscription is handled, you can use the SubscribeCoreAsync() method instead.

await using NatsConnection nc = new NatsConnection();

// Connections are lazy, so we need to connect explicitly
// to avoid any races between subscription and publishing.
await nc.ConnectAsync();

await using INatsSub<int> sub = await nc.SubscribeCoreAsync<int>("foo");

for (int i = 0; i < 10; i++)
{
    Console.WriteLine($" Publishing {i}...");
    await nc.PublishAsync<int>("foo", i);
}

// Signal subscription to stop
await nc.PublishAsync<int>("foo", -1);

// Messages have been collected in the subscription internal channel
// now we can drain them
await foreach (NatsMsg<int> msg in sub.Msgs.ReadAllAsync())
{
    Console.WriteLine($"Received {msg.Subject}: {msg.Data}\n");
    if (msg.Data == -1)
        break;
}

// We can unsubscribe from the subscription explicitly
// (otherwise dispose will do it for us)
await sub.UnsubscribeAsync();

Round Trip Time (RTT)

PingAsync() is somewhat a special method in all NATS clients, mostly referred to as rtt. It is used to send a ping to the server and receive a pong back while measuring the round trip time. Since it waits for the server to respond, as a side effect, it also flushes the outgoing buffers.

Remember that every NatsConnection instance is a single TCP connection and all the calls sent to the server are essentially sent back to back after they're picked up from internal queues and buffers.

await using NatsClient nc = new NatsClient();

TimeSpan rtt = await nc.PingAsync();

Console.WriteLine($"RTT to server: {rtt}");
Note

NatsConnection establishes the first server connection when the first call to subscribe or publish is made. You can also call the ConnectAsync() method explicitly to establish the connection before any other calls are made.

What's Next

  • Serialization is the process of converting an object into a format that can be stored or transmitted.
  • Security is an important aspect of any distributed system. NATS provides a number of security features to help you secure your applications.
  • AOT Deployment is a way to deploy your applications as native platform executables, which produces faster startup times and better performance in most cases.