Capítulo 14: Mensageria Assíncrona com RabbitMQ ou Apache Kafka
Especialização em Backend com Go & Gin • Gin Framework & Go 1.22+ • GORM, Clean Architecture, Concorrência e Alta Performance
🗺️ Mapa Conceitual do Tópico
flowchart TD
A["Cliente HTTP / Frontend"] --> B["API Gateway / Router"]
B --> C["Controller / Handler"]
C --> D["Service Layer (Regras de Negócio)"]
D --> E["Repository / ORM (Persistência)"]
E --> F["Banco de Dados / Cache"]
subgraph ARQ["Arquitetura do Capítulo"]
G["Conceito: Mensageria Assíncrona com RabbitMQ ou Apache Kafka"]
H["Segurança, Validação e Resiliência"]
I["Alta Performance e Escalabilidade"]
end
D --> ARQ
style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
style B fill:#fff3e0,stroke:#ff9800,stroke-width:2px
style C fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
style D fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
style E fill:#fce4ec,stroke:#e91e63,stroke-width:2px
style F fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px
🏛️ 1. Fundamentos Técnicos de Mensageria Assíncrona com RabbitMQ ou Apache Kafka
Mensageria assíncrona desacopla o produtor de um evento do(s) consumidor(es), permitindo que serviços processem trabalho sem bloquear a requisição HTTP original e sem conhecimento direto uns dos outros. RabbitMQ e Apache Kafka resolvem esse problema com modelos de arquitetura fundamentalmente diferentes, e escolher entre eles é uma decisão de design, não uma preferência estilística:
- RabbitMQ é um message broker tradicional (protocolo AMQP): mensagens são roteadas por exchanges até filas, entregues a um consumidor e removidas da fila após confirmação (
Ack). É ideal para filas de tarefas, roteamento condicional complexo e cenários onde a mensagem representa um comando único a ser processado uma vez. - Apache Kafka é uma plataforma de streaming de eventos: mensagens são anexadas a um log de commit imutável, particionado, e retidas por um período configurável independentemente de terem sido consumidas. Múltiplos grupos de consumidores podem ler o mesmo tópico de forma independente e reprocessar o histórico. É ideal para event sourcing, auditoria e pipelines de dados de altíssimo throughput.
Em Go, o cliente idiomático para RabbitMQ é github.com/rabbitmq/amqp091-go. O publicador declara a fila (QueueDeclare, idempotente) e publica com PublishWithContext, definindo DeliveryMode: amqp.Persistent para que a mensagem sobreviva a um restart do broker. Do lado do consumidor, a confiabilidade depende do acknowledgement manual: msg.Ack(false) confirma que a mensagem foi processada com sucesso e pode ser removida da fila; se o worker falhar ou crashar antes do Ack, o RabbitMQ reenfileira automaticamente a mensagem para outro consumidor — chamando msg.Nack(false, true) explicitamente quando o processamento falha de forma recuperável (o segundo parâmetro requeue=true decide se ela volta para a fila ou é descartada/roteada para DLQ).
Esse modelo “pelo menos uma vez” (at-least-once) implica que handlers de consumo devem ser idempotentes — processar a mesma mensagem duas vezes (por exemplo, após um crash entre o processamento e o Ack) não pode gerar efeito colateral duplicado (cobrar duas vezes, criar dois pedidos).
Um problema clássico de integração é o dual-write: gravar no banco de dados e publicar um evento são duas operações contra sistemas diferentes que não compartilham uma transação — se o processo cair entre as duas, o sistema fica inconsistente (dado salvo, evento nunca publicado, ou vice-versa). O padrão Outbox resolve isso: o evento é gravado como uma linha em uma tabela outbox_events, na mesma transação SQL que grava o dado de negócio (atomicidade garantida pelo próprio banco); um processo separado (poller ou CDC) lê a tabela outbox periodicamente e publica as mensagens pendentes no broker, marcando-as como processadas — garantindo consistência eventual sem exigir transação distribuída (2PC).
Para lidar com mensagens que falham repetidamente (payload malformado, bug no handler — as chamadas “poison messages”), configura-se uma Dead Letter Exchange (DLX): a fila principal declara os argumentos x-dead-letter-exchange e x-dead-letter-routing-key; após esgotar um número de tentativas (ou expirar via x-message-ttl), o RabbitMQ roteia automaticamente a mensagem para uma fila de dead-letter (DLQ), isolando-a para inspeção manual sem travar o processamento das mensagens saudáveis que vêm atrás dela na fila principal.
💻 2. Código de Demonstração Corporativo
package messaging
import (
"context"
"encoding/json"
"log"
amqp "github.com/rabbitmq/amqp091-go"
)
type PedidoCriadoEvent struct {
PedidoID uint `json:"pedido_id"`
Total float64 `json:"total"`
}
// PublicarPedidoCriado serializa o evento e publica de forma persistente,
// sobrevivendo a um restart do broker (DeliveryMode: Persistent).
func PublicarPedidoCriado(ch *amqp.Channel, evento PedidoCriadoEvent) error {
body, err := json.Marshal(evento)
if err != nil {
return err
}
return ch.PublishWithContext(context.Background(), "", "pedidos_queue", false, false, amqp.Publishing{
ContentType: "application/json",
DeliveryMode: amqp.Persistent,
Body: body,
})
}
// ConsumirPedidos processa mensagens com Ack manual: só remove da fila após
// sucesso; em falha recuperável, Nack com requeue devolve para reprocessamento.
func ConsumirPedidos(msgs <-chan amqp.Delivery) {
for d := range msgs {
var evento PedidoCriadoEvent
if err := json.Unmarshal(d.Body, &evento); err != nil {
d.Nack(false, false) // payload malformado: descarta (vai para DLX, se configurada)
continue
}
if err := processarPedido(evento); err != nil {
log.Printf("falha ao processar pedido %d: %v", evento.PedidoID, err)
d.Nack(false, true) // erro recuperável: reenfileira
continue
}
d.Ack(false)
}
}
🔗 Recursos Pedagógicos do Capítulo 14
| Recurso Didático | Finalidade | Link de Acesso |
|---|---|---|
| 📊 Slides de Aula | Apresentação visual interativa com Dark Mode e suporte a teclado | Ver Slides |
| 🧠 Quiz Formativo | Teste interativo de fixação com feedback imediato por alternativa | Fazer Quiz |
| 💻 Exemplos de Código | Demonstrações funcionais com código executável | Ver Exemplos |
| 🧩 Exercícios em 4 Níveis | Lista progressiva de fixação com gabarito em bloco colapsável | Resolver Exercícios |
| ⬅️ Capítulo Anterior | 📚 Sumário de Tópicos | Próximo Capítulo ➡️ |