RabbitMQ cluster on Docker with Golang in 5 minutes

If you are looking for a way to integrate you Golang application with RabbitMQ queue then you've found it!

What is RabbitMQ?

RabbitMQ is a highly available message queue build for high troughput and low latency supporting multiple protocols. What does that mean?

Message queue

Simply put, it's a service to which you can send a message containing some data and read this message any time. In addition you can send messages in parallel and at the same time read them, also in parallel. Message queue is a service that allows you to have control over the flow of data in your application. Scale the infrastructure that handles it's processing and make sure no data is lost.

Highly available

RabbitMQ supports data replication, which means that it's able to copy the data you send to it to multple hosts. In case of a failure of one of the nodes, your data is safe and the processing is uninterrupted.

High throughput

RabbitMQ can handle multiple millions of messages per second.

Low latency

Each message in RabbitMQ is available for processing immediately, if you provide enough infrastructure that can answer the load, RabbitMQ will not be a bottleneck.

Multiple protocols

RabbitMQ can speak multiple protocols, AMQP, STOMP, MQTT, HTTP/Websocket

RabbitMQ cluster on Docker

I have already described in detail how to setup RabbitMQ cluster with Docker. Please read the below article for more details: Klaster RabbitMQ na Dockerze z Nodejs w 5 minut .

Below is an excerpt of the command to be run in order to setup a cluster.

# pull docker images
docker pull 3.6.6-management

# start docker image and attach to the shell
docker run --hostname rabbit --name rabbit --rm -ti --net="host" rabbitmq:3.6.6-management /bin/bash

# start three distincs RabbitMQ processes identified by different names
RABBITMQ_NODE_PORT=5672 RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener [{port,15672}]" RABBITMQ_NODENAME=rabbit rabbitmq-server -detached
RABBITMQ_NODE_PORT=5673 RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener [{port,15673}]" RABBITMQ_NODENAME=hare rabbitmq-server -detached
RABBITMQ_NODE_PORT=5674 RABBITMQ_SERVER_START_ARGS="-rabbitmq_management listener [{port,15674}]" RABBITMQ_NODENAME=john rabbitmq-server -detached

# join `hare` node to `rabbit` cluster
rabbitmqctl -n hare stop_app
rabbitmqctl -n hare join_cluster rabbit@`hostname -s`
rabbitmqctl -n hare start_app

# join `john` node to `rabbit` cluster
rabbitmqctl -n john stop_app
rabbitmqctl -n john join_cluster rabbit@`hostname -s`
rabbitmqctl -n john start_app

Connect to RabbitMQ with Golang. Create Producer script

package main

import (
    "log"

    "github.com/streadway/amqp"
)

func failOnError(err error, msg string) {
    if err != nil {
        log.Fatalf("%s: %s", msg, err)
    }
}

func main() {
    conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
    failOnError(err, "Failed to connect to RabbitMQ")
    defer conn.Close()

    ch, err := conn.Channel()
    failOnError(err, "Failed to open a channel")
    defer ch.Close()

    q, err := ch.QueueDeclare(
        "hello", // name
        false,   // durable
        false,   // delete when unused
        false,   // exclusive
        false,   // no-wait
        nil,     // arguments
    )
    failOnError(err, "Failed to declare a queue")

    body := "Hello World!"
    err = ch.Publish(
        "",     // exchange
        q.Name, // routing key
        false,  // mandatory
        false,  // immediate
        amqp.Publishing{
            ContentType: "text/plain",
            Body:        []byte(body),
        })
    log.Printf(" [x] Sent %s", body)
    failOnError(err, "Failed to publish a message")
}

Create Consumer script

package main

import (
    "log"

    "github.com/streadway/amqp"
)

func failOnError(err error, msg string) {
    if err != nil {
        log.Fatalf("%s: %s", msg, err)
    }
}

func main() {
    conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
    failOnError(err, "Failed to connect to RabbitMQ")
    defer conn.Close()

    ch, err := conn.Channel()
    failOnError(err, "Failed to open a channel")
    defer ch.Close()

    q, err := ch.QueueDeclare(
        "hello", // name
        false,   // durable
        false,   // delete when unused
        false,   // exclusive
        false,   // no-wait
        nil,     // arguments
    )
    failOnError(err, "Failed to declare a queue")

    msgs, err := ch.Consume(
        q.Name, // queue
        "",     // consumer
        true,   // auto-ack
        false,  // exclusive
        false,  // no-local
        false,  // no-wait
        nil,    // args
    )
    failOnError(err, "Failed to register a consumer")

    forever := make(chan bool)

    go func() {
        for d := range msgs {
            log.Printf("Received a message: %s", d.Body)
        }
    }()

    log.Printf(" [*] Waiting for messages. To exit press CTRL+C")
    <-forever
}

Run both scripts

Now that we have our script ready we can run each of them. Create two separate files as distinct Go modules. Run `send.go` first to produce a message

/go/src/rabbit-example/producer# go run send.go
2020/12/05 21:14:14  [x] Sent Hello World!

And the run `receive.go` to consume produced message

/go/src/rabbit-example/consumer# go run receive.go
2020/12/05 21:14:39  [*] Waiting for messages. To exit press CTRL+C
2020/12/05 21:14:39 Received a message: Hello World!

That's it we have implemented a RabbitMQ into Go code in 5 minutes, plain and simple. RabbitMQ has many different options and setting and you should spend some time reading the documentation to fully understand how it works and what should be the perfect setting for your use case.

Thanks for reading, I want to provide more articles about implementing RabbitMQ in Golang applications, highlighting especially low latency workers with multiple queues. Let me know if you are interested!



Similar searches: rabbitmq golang / go queue rabbitmq / implement rabbitqm in golang application

These posts might be interesting for you:

  1. Klaster RabbitMQ na Dockerze z Nodejs w 5 minut
  2. 3-węzłowy zestaw repliki MongoDB z SystemD i metrykami w Telegraf / Grafana
Author: Peter

I'm a backend programmer for over 10 years now, have hands on experience with Golang and Node.js as well as other technologies, DevOps and Architecture. I share my thoughts and knowledge on this blog.