When building a microservice architecture, you have basically two ways you can build communication between its elements. First, the obvious one is to make services call each other directly via eg. using HTTP endpoints, while the other is to have a message bus/queue where one app publishes a message, while others read it. This post explains the basics of one of the message bus solutions, NATS.

Basic NATS concepts

NATS is a popular and popular, yet easy-to-use messaging system with uses a text-based protocol which makes it easy to create and extend clients in various programming languages. Since it's written in Go, it obviously provides a client for it as well.

According to its documentation, NATS is very fast and lightweight, always available and allows three messaging models:

  • publish-subscribe (described in this post),
  • request-reply
  • queue

Also, with NATS Streaming (build on top of the basic version) you get events streaming and persistent (guaranteed) message delivery.

To showcase the features of NATS and the way you can use it with Go, we have the following example: one of the services acts as a blog admin where you can request publishing a new article, while the other, blog generator creates static HTML files with those articles and serves them to the users. The way we want it to work, admin publishes a message to NATS, which is later read by generator and static files are created.

Since NATS Go client requires the message to be a slice of bytes, I chose to use protocol buffers for efficient marshaling, and both admin and generator will be importing the same protobuf-generated code.

Publishing messages

First of all, the publisher needs to connect to the NATS server using nats.Connect(..) function, which uses configuration functions to alter the Options used to set up the connection. If you want to use the default settings, all you need to do is type:

natsClient, err := nats.Connect(cfg.NatsAddr)

Now, in order to publish a message, we first need to marshal it into a slice of bytes:

message := &pb.PublishPostMessage{
    Title:   pubReq.Title,
    Content: pubReq.Content,
}

bs, err := proto.Marshal(msg)

Then, we need to choose the topic name and Publish it:

const topicPublishPost = "posts:publish"
...
err := natsClient.Publish(topicPublishPost, bs)
...

After publishing the message we still need to flush data to the underlying buffer to have it actually sent to the NATS server. After that we can ask NATS if there were any errors while doing so, to make sure the message was dispatched correctly:

err := natsClient.Flush()
...

err := natsClient.LastError()
...

In our example, the blog admin service connects to NATS server on startup, then exposes an HTTP server where you can request an article to be published. In the handler, a NATS message is built and dispatched, while the user gets a response with the information whether the publishing part went well (no errors on Publish(), Flush() and LastError() calls).

Subscribing to messages (subjects)

As with the publisher, the subscriber begins with connecting to NATS server:

natsClient, err := nats.Connect(cfg.NatsAddr)

Then, the only other NATS-related thing you need to do is create a subscription where you define a topic you want to listen to and an action that should be performed when the message arrives. To do the latter you use nats.MsgHandler function which takes the messages as its only input argument:

subs, err := natsClient.Subscribe(topic, func(msg *nats.Msg){
    ...
})
...

That's it!

In our example, the generator listens to posts:publish topic, then creates an HTML file using an awesome, Go's built-in templates and puts it in a directory that is served by an HTTP server which is also a part of the service.

Example in action

In order to see that everything works as planned, we first request to see a non-existing blog post:

$ curl -X GET http://localhost:8080/hello-mycodesmells.html

    404 page not found

Then we ask blog admin to actually create the article:

$ curl -X POST \
http://localhost:9000/publish \
-d '{
        "title": "Hello MyCodeSmells",
        "content": "Hello! This is an awesome blog post for MyCodeSmells readers =)"
}'

    Post publication is pending

Now when we ask for the blog post, we see a pretty HTML output:

curl -X PGET http://localhost:8080/hello-mycodesmells.html

    <html>

    <head>
        <meta charset="utf-8">
        <title>Hello MyCodeSmells</title>
    </head>

    <body>
        <h2>Hello MyCodeSmells</h2>
        <small>Published on 04-15-2018</small>
        <p>
            Hello! This is an awesome blog post for MyCodeSmells readers =)
        </p>
    </body>

    </html>

As you can see, it works like a charm! The full source code of this example is available on Github.

Links

Versions

  • go -> 1.10
  • protoc -> 3.0.0
  • nats-server -> 1.1.0