Microservices Communication with NATS

Microservices architectures enable the development of scalable and independent services within the system, promoting agility and robustness. Communication between these services is crucial for the overall performance and functionality of the application. NATS is a high-performance messaging system that is ideal for the microservices architecture due to its simplicity, scalability, and speed. It supports various communication patterns and it can include request-reply and publish-subscribe.

NATS server can provide lightweight and flexible messaging that is perfect for the microservices talking to each other asynchronously. It supports the various messaging models, but we will focus primarily on the publish-subscribe model.

NATS

NATS is a high-performance messaging system specifically designed for modern distributed systems like microservices. It can offer several features that make it appealing to such architectures:

  • Lightweight and Fast: NATS is built with performance in mind and it can ensure the messages are delivered quickly even under the high loads.
  • Scalability: It can supports the one-to-many and many-to-one communication patterns and scaling up the seamlessly as the system grows.
  • Fault Tolerance: NATS supports the clustering which can provides the robustness and high availability.
  • Polyglot: It can offers the clients in the multiple languages and making it versatile for the diverse application ecosystems.

Messaging Patterns Supported by NATS

NATS can primarily supports three messaging patterns:

  • Publish-Subscribe: In this model, messages are published to the subject and any active subscriber to that the subject receives the message. This is the useful for the broadcasting messages to the multiple recipients.
  • Request-Reply: This is more direct, where the sender expects the response from the receiver. It is useful for the synchronous operations where the sender needs the information back from the receiver.
  • Queueing: Multiple subscribers can listen on the same subject but the each message is processed only one of them. It is useful for the load balancing tasks among the multiple workers.

Integration of NATS with Spring Boot

We need to set up and configure the Spring Boot application to the connect with NATS server.

  • Dependencies: Include the NATS client libraries into the Spring project.
  • Configuration class: Create the Spring configuration class that establishes and exposes the NATS connection as the Spring bean.

Implementation Details with NATS

Publishing Messages

To publish the messages:

  • Create the Publisher Service: This service will have the method to the send messages to the specified NATS subject.
  • Use the NATS connection: We can utilize the NATS connection to publish the messages. The method can takes the subject and message content and converting the content to bytes if necessary and sends it over the network.

Subscribing to Messages

For subscribing to messages:

  • Create the Subscriber Service: This service can sets up the subscriptions to the listen for the messaging on specified subjects.
  • Handling the messages: Define how received the messages should be processed. It can typically involves the converting the message bytes back to the String format and then executing the business logic.

Implementation of Microservices Communication with NATS

We will create the simple example project using the Spring Boot for the microservices scenario. We will create the two services: a Publisher Service that can sends the messages about the new orders and the Subscriber Service that can receives these messages and processes them.

Setup the NATS Server

We will setup the NATS server using the below command into the command prompt on your local system.

docker run -d --name nats-server -p 4222:4222 -p 8222:8222 nats

Command Prompt Output:


NATS Dashboard API:

http://localhost:8222

Output:


NATS Microservice Demo Project

Step 1: Create a new Spring Boot project using Spring Initializr, and include the following dependencies:

  • Spring web
  • Lombok
  • Spring DevTools

External Dependency:

        <!-- https://mvnrepository.com/artifact/io.nats/nats-spring -->
<dependency>
<groupId>io.nats</groupId>
<artifactId>nats-spring</artifactId>
<version>0.5.7</version>
</dependency>

Once the project is created, the file structure will resemble the image below.


Step 2: Open the application.properties file and add the server port connection properties to the project.

spring.application.name=nats-microservices-demo
server.port=8080


Step 3: Create the NatsConfig configuration class

We’ll create the NatsConfig class to configure to the NATS server of the application.

Go to src > org.example.natsmicroservicesdemo > config > NatsConfig and put the below code.

Java
package org.example.natsmicroservicesdemo.config;

import io.nats.client.Connection;
import io.nats.client.Nats;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.io.IOException;

@Configuration
public class NatsConfig {

    @Bean
    public Connection natsConnection() throws IOException, InterruptedException {
        return Nats.connect("nats://localhost:4222");
    }
}


Step 4: Create the Subscriber Service class

We’ll create the SubscriberService class to subscribe the messages to the publisher service of the application.

Go to src > org.example.natsmicroservicesdemo > service > SubscriberService and put the below code.

Java
package org.example.natsmicroservicesdemo.service;



import io.nats.client.Connection;
import io.nats.client.Dispatcher;
import jakarta.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;



@Service
public class SubscriberService {

    @Autowired
    private Connection natsConnection;

    @PostConstruct
    public void subscribeToSubject() {
        Dispatcher dispatcher = natsConnection.createDispatcher((msg) -> {
            String response = new String(msg.getData());
            System.out.println("Received message on '" + msg.getSubject() + "': " + response);
        });

        dispatcher.subscribe("order_updates");
    }
}


Step 5: Create the Publisher Service class

We’ll create the PublisherService class to publish the messages to the subscriber service of the application.

Go to src > org.example.natsmicroservicesdemo > service > PublisherService and put the below code.

Java
package org.example.natsmicroservicesdemo.service;


import io.nats.client.Connection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class PublisherService {

    @Autowired
    private Connection natsConnection;

    public void publishMessage(String subject, String message) {
        natsConnection.publish(subject, message.getBytes());
    }
}


Step 6: Create the Message Controller class

We’ll create the controller class to send messages to the publish service.

Go to src > org.example.natsmicroservicesdemo > controller > MessageController and put the below code.

Java
package org.example.natsmicroservicesdemo.controller;


import org.example.natsmicroservicesdemo.service.PublisherService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MessageController {

    @Autowired
    private PublisherService publisherService;

    @GetMapping("/publish")
    public String publishMessage(@RequestParam String message) {
        publisherService.publishMessage("order_updates", message);
        return "Message sent: " + message;
    }
}


Step 7: Open the Main Class and insert the following code.

Note: No change are required keep it default code of the project.

Java
package org.example.natsmicroservicesdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class NatsMicroservicesDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(NatsMicroservicesDemoApplication.class, args);
    }

}


pom.xml:

XML
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.5</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>org.example</groupId>
    <artifactId>nats-microservices-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>nats-microservices-demo</name>
    <description>nats-microservices-demo</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/io.nats/nats-spring -->
        <dependency>
            <groupId>io.nats</groupId>
            <artifactId>nats-spring</artifactId>
            <version>0.5.7</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>


Step 8: Run the application

Once we run the application, then the project will run at port 8080.


Endpoint Testing

GET http://localhost:8080/publish?subject=updates&message=Hello%20NATS

Output:


Application Log: