Introduction
In the world of distributed systems, inter-process communication is vital for the functionality and reliability of applications. One effective way to achieve this is through message brokers. RabbitMQ, an open-source message broker that implements the Advanced Message Queuing Protocol (AMQP), is a popular choice for building scalable and decoupled systems. This article will walk you through building simple applications in Go that communicate with each other via RabbitMQ.
Prerequisites
Before we dive into the code, make sure you have the following:
-
Go (Golang): Make sure Go is installed on your machine. If not, download it from the official site.
-
RabbitMQ: You can either install RabbitMQ locally or use Docker to get it up and running. If using Docker, you can run the following command:
docker run -d --hostname my-rabbit --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3-management
-
Go RabbitMQ Client: To connect our Go applications to RabbitMQ, we will use the
github.com/streadway/amqp
package.To get started, initialize your Go module:
go mod init rabbitmq-demo go get github.com/streadway/amqp
Application Overview
We’ll create two simple Go applications: a Sender and a Receiver. The Sender will send messages to a RabbitMQ queue, and the Receiver will consume those messages.
Sender Application
Create a file named sender.go
:
package main
import (
"log"
"time"
"github.com/streadway/amqp"
)
func main() {
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
if err != nil {
log.Fatalf("Failed to connect to RabbitMQ: %s", err)
}
defer conn.Close()
ch, err := conn.Channel()
if err != nil {
log.Fatalf("Failed to open a channel: %s", err)
}
defer ch.Close()
queue, err := ch.QueueDeclare(
"hello", // name
false, // durable
false, // delete when unused
false, // exclusive
false, // no-wait
nil, // arguments
)
if err != nil {
log.Fatalf("Failed to declare a queue: %s", err)
}
for i := 0; i < 10; i++ {
body := "Hello RabbitMQ!" + string(i)
err = ch.Publish(
"", // exchange
queue.Name, // routing key
false, // mandatory
false, // immediate
amqp.Publishing{
ContentType: "text/plain",
Body: []byte(body),
})
if err != nil {
log.Fatalf("Failed to publish a message: %s", err)
}
log.Printf("Sent: %s", body)
time.Sleep(1 * time.Second) // sleep for a second
}
}
In this code, we’re establishing a connection to RabbitMQ, declaring a queue, and sending ten messages to that queue with a 1-second interval.
Receiver Application
Create another file named receiver.go
:
package main
import (
"log"
"github.com/streadway/amqp"
)
func main() {
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
if err != nil {
log.Fatalf("Failed to connect to RabbitMQ: %s", err)
}
defer conn.Close()
ch, err := conn.Channel()
if err != nil {
log.Fatalf("Failed to open a channel: %s", err)
}
defer ch.Close()
queue, err := ch.QueueDeclare(
"hello", // name
false, // durable
false, // delete when unused
false, // exclusive
false, // no-wait
nil, // arguments
)
if err != nil {
log.Fatalf("Failed to declare a queue: %s", err)
}
msgs, err := ch.Consume(
queue.Name, // queue
"", // consumer
true, // auto-ack
false, // exclusive
false, // no-local
false, // no-wait
nil, // args
)
if err != nil {
log.Fatalf("Failed to register a consumer: %s", err)
}
go func() {
for d := range msgs {
log.Printf("Received a message: %s", d.Body)
}
}()
log.Printf("Waiting for messages. To exit press CTRL+C")
select {} // block forever
}
In this application, we connect to RabbitMQ and set up a consumer that listens for messages on the same queue. It will print out any message it receives.
Running the Applications
Now, let’s run both applications:
-
Start the RabbitMQ server if you haven’t already.
-
Open one terminal and run the Receiver:
go run receiver.go
-
Open another terminal and run the Sender:
go run sender.go
Expected Output
The Receiver will output the messages sent by the Sender, each prefixed by “Received a message”.
Conclusion
You have successfully created two simple Go applications that communicate using RabbitMQ. This demonstrates a fundamental aspect of distributed applications, allowing for decoupling of services by using message queues.
Feel free to expand on this example by adding more features, such as message acknowledgment, error handling, or scaling up with more receiver instances.
Further Reading and Resources
By exploring these resources, you will gain deeper insights into developing scalable applications using Go and RabbitMQ.