Micronaut Nats

Integration between Micronaut and nats.io

Version:

1 Introduction

This project includes integration between Micronaut and nats.io. The standard Java Client is used to do the actual publishing and consuming.

2 Release History

For this project, you can find a list of releases (with release notes) here:

3 Using the Micronaut CLI

To create a project with NATS support using the Micronaut CLI, supply the nats feature to the features flag.

$ mn create-app my-nats-app --features nats

This will create a project with the minimum necessary configuration for NATS.

Messaging Application

The Micronaut CLI can generate messaging applications. This will create a Micronaut app with NATS support, and without an HTTP server (although you can add one if you desire). The profile also provides a couple commands for generating NATS consumers and producers.

To create a NATS messaging application, use the the following command:

$ mn create-messaging-app my-nats-service --features nats

As you’d expect, you can start the application with ./gradlew run (for Gradle) or ./mvnw compile exec:exec (Maven). The application will (with the default config) attempt to connect to NATS at nats://localhost:4222, and will continue to run without starting up an HTTP server. All communication to/from the service will take place via NATS producers and/or consumers.

Within the new project, you can now run the NATS specific code generation commands:

$ mn create-nats-producer Message
| Rendered template Producer.java to destination src/main/java/my/nats/app/MessageProducer.java

$ mn create-nats-listener Message
| Rendered template Listener.java to destination src/main/java/my/nats/app/MessageListener.java

4 NATS Quick Start

To add support for NATS.io to an existing project, you should first add the Micronaut NATS configuration to your build configuration. For example:

implementation("io.micronaut.nats:micronaut-nats")
<dependency>
    <groupId>io.micronaut.nats</groupId>
    <artifactId>micronaut-nats</artifactId>
</dependency>

Creating a NATS Producer with @NatsClient

To create a NATS Producer that sends messages you can simply define an interface that is annotated with @NatsClient.

For example the following is a trivial @NatsClient interface:

ProductClient.java
import io.micronaut.nats.annotation.NatsClient;
import io.micronaut.nats.annotation.Subject;
import io.micronaut.messaging.annotation.Body;

@NatsClient (1)
public interface ProductClient {

    @Subject("my-products") (2)
    void send(String name);

    void sendProduct(@Subject String brand, @Body String name); (3)
}
1 The @NatsClient annotation is used to designate this interface as a client
2 The @Subject annotation indicates which subject the Message should be published to
3 It is also possible for the subject to be dynamic by making it a method argument

At compile time Micronaut will produce an implementation of the above interface. You can retrieve an instance of ProductClient either by looking up the bean from the ApplicationContext or by injecting the bean with @Inject:

Using ProductClient
ProductClient client = applicationContext.getBean(ProductClient.class);
client.sendProduct("Nike", "Blue Trainers");

Creating a NATS Consumer with @NatsListener

To listen to NATS messages you can use the @NatsListener annotation to define a message listener.

The following example will listen for messages published by the ProductClient in the previous section:

ProductListener.java
import io.micronaut.messaging.annotation.Body;
import io.micronaut.nats.annotation.NatsListener;
import io.micronaut.nats.annotation.Subject;

@NatsListener (1)
public class ProductListener {

    @Subject("my-products") (2)
    public void receive(@Body String name) { (3)
        System.out.println("Got Product - " + name);
    }
}
1 The @NatsListener is used to designate this class as a listener.
2 The @Subject annotation is again used to indicate which subject to subscribe to.
3 The receive method defines one argument, which will receive the value.

5 Configuring the connection

All properties on the Options are available to be modified, either through configuration or a BeanCreatedEventListener.

The properties that can be converted from the string values in a configuration file can be configured directly.

🔗
Table 1. Configuration Properties for SingleNatsConnectionFactoryConfig
Property Type Description

nats.addresses

java.util.List

The list of addresses

nats.username

java.lang.String

the username

nats.password

java.lang.String

the password

nats.token

java.lang.String

the token

nats.max-reconnect

int

times to try reconnect

nats.reconnect-wait

java.time.Duration

time to wait

nats.connection-timeout

java.time.Duration

maximumTime for inital connection

nats.ping-interval

java.time.Duration

time between server pings

nats.reconnect-buffer-size

long

size of the buffer, in bytes, used to store publish messages during reconnect

nats.inbox-prefix

java.lang.String

custom prefix for request/reply inboxes

nats.no-echo

boolean

enable or disable echo messages, messages that are sent by this connection back to this connection

nats.utf8support

boolean

whether or not the client should support for UTF8 subject names

nats.credentials

java.lang.String

path to the credentials file to use for authentication with an account enabled server

🔗
Table 2. Configuration Properties for ClusterNatsConnectionFactoryConfig
Property Type Description

nats.servers.*.addresses

java.util.List

The list of addresses

nats.servers.*.username

java.lang.String

the username

nats.servers.*.password

java.lang.String

the password

nats.servers.*.token

java.lang.String

the token

nats.servers.*.max-reconnect

int

times to try reconnect

nats.servers.*.reconnect-wait

java.time.Duration

time to wait

nats.servers.*.connection-timeout

java.time.Duration

maximumTime for inital connection

nats.servers.*.ping-interval

java.time.Duration

time between server pings

nats.servers.*.reconnect-buffer-size

long

size of the buffer, in bytes, used to store publish messages during reconnect

nats.servers.*.inbox-prefix

java.lang.String

custom prefix for request/reply inboxes

nats.servers.*.no-echo

boolean

enable or disable echo messages, messages that are sent by this connection back to this connection

nats.servers.*.utf8support

boolean

whether or not the client should support for UTF8 subject names

nats.servers.*.credentials

java.lang.String

path to the credentials file to use for authentication with an account enabled server

Without any configuration the defaults in the Options will be used.
It is also possible to disable the integration entirely with nats.enabled: false

Connections

It is possible to configure multiple connections to the same server, different servers, or a single connection to one of a list of servers.

nats:
    server1:
      addresses:
        - "nats://localhost:4222"
      username: guest
      password: guest
    server2:
      addresses:
        - "nats://randomServer:4222"
      username: guest
      password: guest

NATS also supports a fail over connection strategy where the first server that connects successfully will be used among a list of servers. To use this option in Micronaut, simply supply a list of host:port addresses.

nats:
    addresses:
      - "nats://localhost:4222"
      - "nats://randomServer:4222"
    username: guest
    password: guest
When the configuration option nats.servers is used, no other options underneath nats are read; for example nats.username.

If you need to setup TLS, it can be configured this way:

nats:
    addresses:
      - "nats://localhost:4222" (1)
    tls:
      trust-store-path:  /path/to/client.truststore.jks (2)
      trust-store-password: secret
      certificate-path: /path/to/certificate.crt (3)
1 You can either use nats://localhost:4222 or tls://localhost:4222 as protocol.
2 You can configure a complete truststore
3 Or ou can use a single certificate for connecting to NATS securely.

6 NATS Producers Using @NatsClient

The example in the quick start presented a trivial definition of an interface that be implemented automatically for you using the @NatsClient annotation.

The implementation that powers @NatsClient (defined by the NatsIntroductionAdvice class) is, however, very flexible and offers a range of options for defining NATS clients.

6.1 Defining @NatsClient Methods

All methods that publish messages to NATS must meet the following conditions:

  • The method must reside in a interface annotated with @NatsClient.

  • The method or a method parameter must be annotated with @Subject.

  • The method must contain an argument representing the body of the message.

In order for all of the functionality to work as designed in this guide your classes must be compiled with the parameters flag set to true. If your application was created with the Micronaut CLI, then that has already been configured for you.

Simple Producer

The easiest way for defining a producer is the following:

@Subject("my-products")
void sendProduct(String name);

Dynamic subject

void sendProduct(@Subject String topic, String name);

Publishing to Queues

The NATS server will route the message to the queue and select a message receiver.

Return Type and RPC

If the producer defines a return type, it automatically will use the RPC logic and will wait for an answer from a consumer
@Subject("product")
Product sendProduct(String name);

@Subject("product")
Single<Product> sendProduct(String name);

@Subject("product")
Maybe<Product> sendProduct(String name);

@Subject("product")
Flowable<Product> sendProduct(String name);

7 NATS Consumers Using @NatsListener

The quick start section presented a trivial example of what is possible with the @NatsListener annotation.

The implementation that powers @NatsListener (defined by the NatsConsumerAdvice class) is, however, very flexible and offers a range of options for consuming NATS message.

7.1 Defining @NatsListener Methods

All methods that consume messages from NATS must meet the following conditions:

  • The method must reside in a class annotated with @NatsListener.

  • The method must be annotated with @Subject.

In order for all of the functionality to work as designed in this guide your classes must be compiled with the parameters flag set to true. If your application was created with the Micronaut CLI, then that has already been configured for you.

Simple Consumer

The easiest way for defining a consumer is the following:

    @Subject("my-products")
    public void receive(String name) {
        System.out.println("Got Product - " + name);
    }

Queue Support

Subscribers may specify queue groups at subscription time. 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. If no listeners are available, the message is discarded.
    @Subject(value = "my-products", queue="my-queue")
    public void receive(String name) {
        System.out.println("Got Product - " + name);
    }

Return Type and RPC

If the consumer defines a return type, it automatically will use the RPC logic and will send the return value to the producer
    @Subject(value = "my-products")
    public Product receive(String name) {
        return new Product(name);
    }

8 NATS Health Indicator

This library comes with a health indicator for applications that are using the management module in Micronaut. See the Health Endpoint documentation for more information about the endpoint itself.

The information reported from the health indicator is under the nats key.

"nats": {
  "status": "UP",
  "details": {
    "servers": ["nats://localhost:4222"]
  }
}
To disable the NATS health indicator entirely, add endpoints.health.nats.enabled: false.

9 Repository

You can find the source code of this project in this repository: