Distributed messaging with NATS

karanpratapsingh

Karan Pratap Singh

Posted on January 20, 2022

Distributed messaging with NATS

Recently, I was building an application where I wanted to take event-driven approach for async communication between the microservices. Usually, I would use Apache Kafka, RabbitMQ, Redis Streams or a managed solution like AWS SNS, Google Cloud Pub/Sub but this time I wanted to keep my implementation easy and cost minimal yet not affect my scalability or increase technical debt for the future. After a few hops on StackShare, I found NATS as a popular alternative and after trying it out, I really liked it. That evening I migrated everything from Apache Kafka to NATS. So, in this article, we'll learn what NATS is and how to get started with it.

What is NATS?

nats

NATS is an open-source messaging system. It provides a simple, secure, and performant communications system for digital systems, services, and devices. The core design principles of NATS are performance, scalability, and ease of use. Its server can run on-premise, in the cloud, at the edge, and even on a Raspberry Pi. NATS can secure and simplify the design and operation of modern distributed systems. It is written in Go and used by companies like Tesla, Paypal, Walmart, and many others. NATS is also part of the Cloud Native Computing Foundation (CNCF).

Note: For more info checkout NATS.io or watch this fantastic Keynote by its creator Derek Collison.

Features

Here are some of the features that I found interesting for my use case:

  • Ease of use
  • Highly performant
  • Zero downtime scaling
  • Self healing and resilient
  • Isolated and secure by default
  • Supports edge, cloud or hybrid deployments.

Getting started

Before we start with any code, let's see what we will implement. We will try to make a simple pub/sub example like shown in the diagram below, and we will use different languages and pretend they are different services.
demo

Setup NATS server

Before anything, we'll need to set up a NATS server. We can do it multiple ways as shown below. Personally, I like to use docker but feel free to set it up however you want. We can also use demo.nats.io which is a demo server provider by NATS authors (please don't use it for production!).

Docker

Here's our docker-compose.yml file:



version: "3.8"

services:
  nats:
    container_name: nats
    image: nats:2.7.0-alpine
    ports:
      - 4222:4222


Enter fullscreen mode Exit fullscreen mode


$ docker compose up


Enter fullscreen mode Exit fullscreen mode

Or we can simply use docker run as well.



$ docker run -p 4222:4222 nats:2.7.0-alpine


Enter fullscreen mode Exit fullscreen mode

Locally

As I'm using macOS, I installed it with brew.



$ brew install nats-server
$ nats-server


Enter fullscreen mode Exit fullscreen mode

Note: Checkout official docs for more installation options.

Output



[18193] 2022/01/20 16:00:05.581377 [INF] Starting nats-server
[18193] 2022/01/20 16:00:05.581992 [INF]   Version:  2.7.0
[18193] 2022/01/20 16:00:05.581996 [INF]   Git:      [not set]
[18193] 2022/01/20 16:00:05.582005 [INF]   Name:     ND2VU7MH7J6RU6RS6JUKKPPCTMRPY35LRBRFT3NLENDZI3TL33PVRR3P
[18193] 2022/01/20 16:00:05.582008 [INF]   ID:       ND2VU7MH7J6RU6RS6JUKKPPCTMRPY35LRBRFT3NLENDZI3TL33PVRR3P
[18193] 2022/01/20 16:00:05.582791 [INF] Listening for client connections on 0.0.0.0:4222
[18193] 2022/01/20 16:00:05.583066 [INF] Server is ready


Enter fullscreen mode Exit fullscreen mode

Client

Now that our NATS server is running, we'll be using Go and Node.js clients to connect to it for a simple demonstration. Not familiar with Go or Node? Don't worry NATS has clients available in over 40 languages!

First, let's init our Go module.



$ go mod init example
$ go get github.com/nats-io/nats.go/@latest
$ touch main.go


Enter fullscreen mode Exit fullscreen mode

Our Go code will act as the subscriber.



package main

import (
    "fmt"

    "github.com/nats-io/nats.go"
)

var subject = "my_subject"

func main() {
    wait := make(chan bool)

    nc, err := nats.Connect(nats.DefaultURL)

    if err != nil {
        fmt.Println(err)
    }

    nc.Subscribe(subject, func(m *nats.Msg) {
        fmt.Printf("Received: %s\n", string(m.Data))
        nc.Publish(m.Reply, []byte("Hello"))
    })

    fmt.Println("Subscribed to", subject)

    <-wait
}


Enter fullscreen mode Exit fullscreen mode

Now, let's init our Node project.



$ npm init -y
$ npm install nats
$ touch index.js


Enter fullscreen mode Exit fullscreen mode

Here is our JavaScript code that will act as the publisher.



const { connect, StringCodec } = require('nats');

const subject = 'my_subject';
const servers = 'localhost:4222';

async function demo() {
  const codec = StringCodec();
  const nc = await connect({ servers });

  nc.publish(subject, codec.encode('Hello there!'));

  console.log('Sent...');

  await nc.drain();
}

demo();


Enter fullscreen mode Exit fullscreen mode

We will subscribe to the my_subject subject by running our Go code first.



$ go run main.go
Subscribed to my_subject


Enter fullscreen mode Exit fullscreen mode

Now, We will run our JavaScript code, which publishes Hello there! message on my_subject subject.



$ node index.js
Sent...


Enter fullscreen mode Exit fullscreen mode

And there it is! We can see the message being received by our subscriber. NATS makes this so simple, yet so powerful!



Subscribed to my_subject
Received: Hello there!

Enter fullscreen mode Exit fullscreen mode




Conclusion

We haven't even scratched the surface in this article, NATS also has a built-in distributed persistence system called JetStream which takes it to a whole another level!

Lastly, I think NATS is a fantastic technology, it scales well from a hobby project to production ready distributed applications. And best of all it's quite easy to get started. I hope this article was helpful in getting you interested!

💖 💪 🙅 🚩
karanpratapsingh
Karan Pratap Singh

Posted on January 20, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Distributed messaging with NATS
microservices Distributed messaging with NATS

January 20, 2022