Mensageria com RabbitMQ e Golang - Conceitos e Primeiro Contato
Source: Dev.to
Introdução
A comunicação entre sistemas mudou bastante nos últimos anos. Antes, era comum que uma aplicação chamasse outra diretamente e esperasse sua resposta. Isso funcionava, mas só até certo ponto. Quanto maior o sistema, maior o acoplamento e menor a tolerância a falhas. Hoje, quando falamos em microsserviços, escalabilidade e resiliência, surge uma alternativa que atende diversos cenários: a mensageria.
A ideia central é simples. Em vez de depender de uma resposta imediata, o serviço envia uma mensagem para uma fila e segue sua vida. Outro serviço, quando puder, lê essa mensagem e faz o trabalho necessário. Isso torna a aplicação mais leve, mais modular e mais preparada para lidar com períodos de carga alta.
Neste artigo, vamos entender o básico sobre mensageria, aprender como usar o RabbitMQ para organizar o fluxo de mensagens e criar exemplos práticos em Go.
O que é Mensageria e por que ela é tão útil
Mensageria é o padrão de comunicação onde sistemas conversam através do envio e consumo de mensagens, e não por chamadas diretas. Isso resolve alguns problemas comuns em arquiteturas tradicionais.
Imagine um serviço de API que precisa realizar diversas etapas a partir de uma única requisição, sendo que uma delas (ou mais) pode sofrer com lentidão devido a conexão instável, grande volume de dados ou processamento mais pesado. Se for feito de forma síncrona, basta um desses passos ficar lento para o serviço todo travar, ou até mesmo ocasionar erros.
A mensageria permite deslocar essas tarefas para outro componente. Assim, a API recebe a requisição, separa o que precisa ser feito naquele momento (processamento síncrono) e o que não precisa ser concluído antes do retorno da API (processamento assíncrono).
Por exemplo, se houver a necessidade dessa requisição enviar um email ao final, a API pode retornar uma mensagem informando que o email será enviado, e designar o disparo desse email a um serviço de mensageria que será executado de forma independente.
Um cenário comum desse exemplo seria o envio do e‑mail de boas‑vindas após um cadastro. Em vez de ser feito de forma síncrona na requisição, pode ser criada uma estrutura similar a esta:
{
"usuario_id": 123,
"acao": "enviar_email_boas_vindas"
}
Essa mensagem vai para uma fila. Depois, outro serviço lê a fila e envia o e‑mail.
O que é RabbitMQ e como ele funciona
RabbitMQ é um message broker. Ele recebe mensagens, organiza‑as em filas e garante que consumidores possam acessá‑las com segurança. RabbitMQ implementa o protocolo AMQP, que define regras claras para envio, roteamento e entrega de mensagens.
O RabbitMQ se destaca pela simplicidade, estabilidade e pela facilidade de adaptação a diferentes cenários — desde pequenas aplicações até sistemas de alta disponibilidade. Dentre alternativas conhecidas estão Kafka, Redis Streams e SQS.
Conceitos básicos
- Produtor (Producer) – quem envia a mensagem
- Consumidor (Consumer) – quem lê e processa a mensagem
- Exchange – recebe a mensagem e a redireciona
- Fila (Queue) – onde a mensagem fica armazenada
- Binding – associação entre uma exchange e uma fila
- Routing key – chave usada para auxiliar no roteamento
O Exchange é a entidade na qual o produtor publica mensagens, que são então roteadas para um conjunto de filas. O tipo de exchange e as propriedades do binding determinam a lógica de roteamento.
Uma Fila no RabbitMQ é uma coleção ordenada de mensagens. As mensagens são enfileiradas (criadas pelo produtor) e desenfileiradas (entregues aos consumidores) de maneira FIFO (“primeiro a entrar, primeiro a sair”).
Exemplo 1 – Conectando ao RabbitMQ com Go
Nota: Este exemplo assume que você já tem Go e RabbitMQ instalados. Consulte a documentação oficial para instruções de instalação.
- RabbitMQ:
- Go:
Crie um módulo Go:
go mod init exemplo/mensageria
Em seguida, crie um arquivo main.go (ou teste.go) com o código abaixo, que abre uma conexão com o RabbitMQ e verifica se está funcionando:
package main
import (
"log"
amqp "github.com/rabbitmq/amqp091-go"
)
func main() {
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
if err != nil {
log.Fatalf("Erro ao conectar: %s", err)
}
defer conn.Close()
log.Println("Conexão bem-sucedida com RabbitMQ!")
}
Instale as dependências e execute:
go get
go run .
Se o serviço RabbitMQ estiver em execução, você verá a mensagem de sucesso no log.
Dica: Ajuste a URL de conexão (
amqp://guest:guest@localhost:5672/) conforme as credenciais e host que você configurou.
Exemplo 2 – Produção e Consumo de uma Mensagem
Neste exemplo criamos duas aplicações independentes: um produtor que envia mensagens para uma fila e um consumidor que lê essas mensagens.
Estrutura dos projetos
# Pasta do produtor
go mod init exemplo/produtor
# Pasta do consumidor
go mod init exemplo/consumidor
Código do Produtor
package main
import (
"context"
"encoding/json"
"log"
"time"
amqp "github.com/rabbitmq/amqp091-go"
)
func failOnError(err error, msg string) {
if err != nil {
log.Panicf("%s: %s", msg, err)
}
}
type User struct {
UserType int `json:"user_type"`
Id int `json:"id"`
Name string `json:"name"`
}
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(
"users", // name
false, // durable
false, // delete when unused
false, // exclusive
false, // no-wait
nil, // arguments
)
failOnError(err, "Failed to declare a queue")
user := User{UserType: 1, Id: 123, Name: "Alice"}
body, err := json.Marshal(user)
failOnError(err, "Failed to marshal JSON")
err = ch.Publish(
"", // exchange
q.Name, // routing key (queue name)
false,
false,
amqp.Publishing{
ContentType: "application/json",
Body: body,
})
failOnError(err, "Failed to publish a message")
log.Printf(" [x] Sent %s", body)
time.Sleep(1 * time.Second) // give time for the message to be sent
}
Código do Consumidor
package main
import (
"log"
amqp "github.com/rabbitmq/amqp091-go"
)
func failOnError(err error, msg string) {
if err != nil {
log.Panicf("%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()
msgs, err := ch.Consume(
"users", // 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
}
Execute primeiro o consumidor e, em seguida, o produtor. O consumidor exibirá a mensagem recebida no log.