How to use ZeroMQ Request-Reply Pattern in Golang
Tags: go, tutorial, beginners, webdev
Published: 2021-09-06 10:45:34

## Overview

I bet many of us have thought about decopulating a backend and splitting it into microservices. Let's say you have a monolithic backend and then you decide to add something like a newsletter and you'd rather have a microservice that has the sole function of sending emails. 

On the internet you will find several solutions to solve this challenge, but one of the most common is the use of a [message broker](https://en.wikipedia.org/wiki/Message_broker). However, not all of us need a solution as advanced as the use of a message broker, it is in these specific cases (smaller applications) that I like to use [ZeroMQ](https://zeromq.org/).

If you don't know ZeroMQ, that's okay because it's a technology that isn't widely shared in the community, so if you want to know more about ZeroMQ, I recommend reading [this](https://intelligentproduct.solutions/technical-software/introduction-to-zeromq/) article, which will give you a better introduction than me.

## Today's example

The idea of today's example is to create a simple Api (server) that will receive a json property that will be the text and then we will send that same text to an app (worker) that will be responsible for calculating the length of this same string. Finally we will return the string length to the Api and then it is sent in the response body.

The framework I'm going to use is [fiber](https://gofiber.io/) and the ZeroMQ client that I'm going to use is [zmq4](https://github.com/pebbe/zmq4).

# Let's code

As you may have already understood, we are going to have two backends. One of the backends we will call a **server**, which will be our Api. The other backend will be the **worker**, which will be our small microservice.

First and foremost, let's install our dependencies:

```sh
go get github.com/gofiber/fiber/v2
go get github.com/pebbe/zmq4
```

Now let's create a simple API:

```go
// @/server/server.go
package main

import "github.com/gofiber/fiber/v2"

func main() {
    app := fiber.New()

    app.Get("/", func(c *fiber.Ctx) error {
        return c.SendString("Hello there 👋")
    })

    app.Listen(":3000")
}
```

We can start by working on our struct of the data coming from the request body, according to the example it should look like this:

```go
type Input struct {
	Text string `json:"text"`
}
```

Now we can import zmq4 into our project and let's create our client.

```go
// @/server/server.go
package main

import (
	"github.com/gofiber/fiber/v2"
	zmq "github.com/pebbe/zmq4"
)

type Input struct {
	Text string `json:"text"`
}

func main() {
    app := fiber.New()
    zctx, _ := zmq.NewContext()

    // ...
}
```

Then we will create our ZeroMQ socket of the Request type and we will accept connections through an address defined by us.

```go
// @/server/server.go
package main

import (
	"github.com/gofiber/fiber/v2"
	zmq "github.com/pebbe/zmq4"
)

type Input struct {
	Text string `json:"text"`
}

func main() {
    app := fiber.New()
    zctx, _ := zmq.NewContext()

    s, _ := zctx.NewSocket(zmq.REQ)
    s.Bind("tcp://*:6666")

    // ...
}
```

Then, in our endpoint, we will change the http verb from Get to Post and then we will parse the data coming from the request body using the `c.BodyParser()` function.

```go
// @/server/server.go
package main

import (
	"github.com/gofiber/fiber/v2"
	zmq "github.com/pebbe/zmq4"
)

type Input struct {
	Text string `json:"text"`
}

func main() {
    app := fiber.New()
    zctx, _ := zmq.NewContext()

    s, _ := zctx.NewSocket(zmq.REQ)
    s.Bind("tcp://*:6666")

    app.Post("/", func(c *fiber.Ctx) error {
        input := new(Input)
        if err := c.BodyParser(input); err != nil {
            panic(err)
        }
        // ...
        return c.SendString("Hello there 👋")
    })

    app.Listen(":3000")
}
```

Now we have to send the text to our websocket.

```go
// @/server/server.go
package main

import (
	"github.com/gofiber/fiber/v2"
	zmq "github.com/pebbe/zmq4"
)

type Input struct {
	Text string `json:"text"`
}

func main() {
    app := fiber.New()
    zctx, _ := zmq.NewContext()

    s, _ := zctx.NewSocket(zmq.REQ)
    s.Bind("tcp://*:6666")

    app.Post("/", func(c *fiber.Ctx) error {
        input := new(Input)
        if err := c.BodyParser(input); err != nil {
            panic(err)
        }

        s.Send(input.Text, 0)
        // ...
        return c.SendString("Hello there 👋")
    })

    app.Listen(":3000")
}
```

Now when we receive the message with the corresponding text length we will send it in the body of the response, if an error has occurred we will treat it.

```go
// @/server/server.go
package main

import (
	"github.com/gofiber/fiber/v2"
	zmq "github.com/pebbe/zmq4"
)

type Input struct {
	Text string `json:"text"`
}

func main() {
    app := fiber.New()
    zctx, _ := zmq.NewContext()

    s, _ := zctx.NewSocket(zmq.REQ)
    s.Bind("tcp://*:6666")

    app.Post("/", func(c *fiber.Ctx) error {
        input := new(Input)
        if err := c.BodyParser(input); err != nil {
            panic(err)
        }

        s.Send(input.Text, 0)

        if msg, err := s.Recv(0); err != nil {
            panic(err)
        } else {
            return c.JSON(fiber.Map{"Length": msg})
        }
    })

    app.Listen(":3000")
}
```

With this we have our **server** finished and now we can start working on our **worker**.

Let's import zmq4 into our project and let's create our client.

```go
// @/worker/worker.go
package main

import (
    zmq "github.com/pebbe/zmq4"
)

func main() {
	zctx, _ := zmq.NewContext()

	// ...
}
```

Then we will create our ZeroMQ socket of the Reply type and we will accept connections through the address that we defined before.

```go
// @/worker/worker.go
package main

import (
    zmq "github.com/pebbe/zmq4"
)

func main() {
	zctx, _ := zmq.NewContext()

	s, _ := zctx.NewSocket(zmq.REP)
	s.Connect("tcp://localhost:6666")

	// ...
}
```

Then let's create a for loop so that it calculates each of the messages we receive via our **server**.

As soon as we receive the message we will log it and then we will calculate the length of the text using the `len()` function, but we have to take into account that before sending the length of the text we have to convert the value to string. 

For this we will use the `strconv.Itoa()` function. As follows:

```go
// @/worker/worker.go
package main

import (
	"log"
	"strconv"

	zmq "github.com/pebbe/zmq4"
)

func main() {
	zctx, _ := zmq.NewContext()

	s, _ := zctx.NewSocket(zmq.REP)
	s.Connect("tcp://localhost:6666")

	for {
		if msg, err := s.Recv(0); err != nil {
			panic(err)
		} else {
			log.Printf("Received: %s\n", msg)
			s.Send(strconv.Itoa(len(msg)), 0)
		}
	}
}
```

Now when testing our Api with a tool similar to Postman, you can send a json object in the request body with the same properties that are defined in our struct Input.

![testing api](https://res.cloudinary.com/dj5iihhqv/image/upload/v1630925114/Kapture_2021-09-06_at_11.43.10-min_euuhbo.gif)

Then you should have something similar to this on your terminal:

![terminal logs](https://res.cloudinary.com/dj5iihhqv/image/upload/v1630925118/Kapture_2021-09-06_at_11.44.18-min_j96rcf.gif)

## Conclusion

As always, I hope you found it interesting. If you noticed any errors in this article, please mention them in the comments. 🧑🏻‍💻

Hope you have a great day! ✌️