Home Comunidade Eventos YouTube Networks Desafios // produtos Academy News CTI | CVE Contato

02 - RabbitMQ (Fila tradicional)

#acknowledge#amqp#dead-letter#exchanges#rabbitmq
video explicativo
Video explicativoEspaco reservado para o video desta anotação — adicione video: https://youtu.be/... no frontmatter da nota.

RabbitMQ (Fila tradicional)

RabbitMQ é um message broker baseado no protocolo AMQP 0-9-1, desenhado em torno do modelo de filas tradicionais com roteamento flexível. O produtor publica em um exchange, o exchange encaminha a mensagem para filas de acordo com regras de binding, e o consumidor retira da fila com confirmação explícita de processamento. É o broker que carrega décadas de uso em produção para filas de tarefas, integração entre sistemas e distribuição de trabalho, com reputação de confiabilidade e de ferramental operacional maduro. Sua relevância em arquitetura de produção vem do equilíbrio: throughput suficiente para a maioria das cargas, latência baixa por mensagem, semântica de fila clássica com ack e requeue, e recursos de roteamento que dispensam código de despacho no produtor. Para times que precisam de fila de trabalho com controle fino de roteamento e sem a complexidade de partições e offsets, RabbitMQ costuma ser a resposta direta.

O modelo AMQP

O AMQP 0-9-1 separa a publicação da fila em três conceitos. O produtor publica a mensagem em um exchange, sempre; nunca direto em uma fila. O exchange roteia a mensagem para filas conforme bindings, que são regras configuradas entre exchange e fila. O consumidor consome de uma fila com confirmação explícita, o ack, que é o que remove a mensagem definitivamente.

Essa separação dá ao RabbitMQ sua principal qualidade: roteamento declarativo. O produtor publica uma mensagem com routing key e attributes, e a decisão de destino fica na topologia do broker, configurável sem redeploys de código. Novos consumidores com interesses novos entram adicionando binding, e o produtor nem fica sabendo.

As mensagens carregam propriedades além do corpo: content_type, headers, delivery_mode, que controla persistência em disco, priority e expiration. Persistência no RabbitMQ é dupla: mensagens marcadas como persistentes e filas declaradas como duráveis sobrevivem a restart do broker; faltar uma das duas condições perde dados no restart, e é um dos erros mais comuns de configuração.

# Consumidor com ack manual e rejeição controlada (pseudocódigo)
connection = amqp.conectar(host="broker", vhost="producao")
canal = connection.channel()
canal.basic_qos(prefetch_count=1)            # distribuição justa entre workers

def callback(ch, metodo, propriedades, corpo):
    try:
        processar(corpo)                     # trabalho real, ex: gerar PDF
        ch.basic_ack(delivery_tag=metodo.delivery_tag)   # remove da fila
    except ErroDeContrato as e:
        # falha permanente: reprocessar não resolve, vai direto para DLQ
        ch.basic_nack(delivery_tag=metodo.delivery_tag, requeue=False)
        log.erro("mensagem para DLQ", motivo=str(e), msg_id=metodo.message_id)
    except ErroTransitorio:
        # falha temporária: requeue com contagem no header, limita tentativas
        tentativas = propriedades.headers.get("x-retry", 0) + 1
        if tentativas >= 5:
            ch.basic_nack(delivery_tag=metodo.delivery_tag, requeue=False)
        else:
            republicar_com_atraso(corpo, tentativas)   # fila de retry com TTL
            ch.basic_ack(delivery_tag=metodo.delivery_tag)

canal.basic_consume(queue="pedidos.faturamento", on_message_callback=callback)
canal.start_consuming()

Exchanges: direct, fanout e topic

O exchange direct entrega a mensagem às filas cujo binding tem exatamente a mesma routing key da mensagem. É o roteamento ponto a ponto: routing key pagamento.aprovado vai para a fila que se inscreveu nessa chave. Serve para despacho direto quando o produtor conhece o tipo da mensagem.

O exchange fanout ignora a routing key e entrega cópias a todas as filas vinculadas. É o broadcast de trabalho ou de eventos: cada fila recebe a mensagem completa, e o processamento de uma não interfere nas outras. É o equivalente do padrão publish-subscribe em forma mais simples.

O exchange topic roteia por padrões de chave com wildcards: o asterisco substitui uma palavra, a cerquilha substitui zero ou mais palavras. Bindings como pedido.*.aprovado ou log.# dão flexibilidade de hierarquia, e é o tipo mais usado quando o domínio de eventos cresce e novos consumidores aparecem com recortes diferentes do mesmo fluxo. O exchange headers roteia por atributos do header em vez da routing key, útil quando o critério de roteamento não cabe em uma string hierárquica.

Exchange Critério de roteamento Uso típico
direct Igualdade exata de routing key Despacho de comandos específicos
fanout Ignora routing key, envia a todos os bindings Broadcast de eventos para N filas
topic Padrões com * e # na routing key Hierarquia de eventos com recortes por interesse
headers Atributos do header da mensagem Roteamento por múltiplos critérios não hierárquicos

Acknowledge, requeue e confiabilidade

O ciclo de confiabilidade do RabbitMQ tem três pontos de confirmação. O produtor ativa publisher confirms e recebe do broker a confirmação de que a mensagem chegou e foi tratada; sem isso, a publicação é fire and forget, e falha de rede apaga mensagens silenciosamente. O broker persiste conforme as flags. O consumidor confirma com basic.ack após processar; enquanto não chega ack, a mensagem permanece marcada como não confirmada e volta para a fila se a conexão do consumidor cai.

O requeue é o comportamento que precisa de cuidado. Quando o consumidor rejeita com basic.nack ou basic.reject e pede requeue, a mensagem volta ao topo da fila imediatamente, e um consumidor com bug reprocessa em loop infinito a mesma falha. O recurso que doma esse problema é a dead letter exchange: uma fila pode declarar que mensagens rejeitadas, expiradas ou excedentes vão para outra exchange, formando a fila de mortos com contexto de motivo.

Prefetch é o controle de fluxo do consumidor: define quantas mensagens não confirmadas ele pode ter em mãos ao mesmo tempo. Sem prefetch, o broker empurra mensagens sem parar e desbalanceia carga entre consumidores; com prefetch baixo demais, o throughput sofre por round trip. O ajuste por perfil de processamento é rotina de operação, e prefetch 1 é o padrão para trabalho pesado com distribuição justa.

TTL, prioridades e ordem

O RabbitMQ suporta TTL por mensagem e por fila, descartando ou roteando para dead letter o conteúdo expirado, o que implementa padrões como retry atrasado: a fila de retry tem TTL e dead letter apontando de volta para a fila principal, criando backoff sem timers no código. Prioridades por mensagem reordenam a entrega dentro da fila declarada com suporte a prioridade, útil para separar trabalho urgente do volume comum.

A ordem de entrega é FIFO no caso normal de fila única com um consumidor. Com múltiplos consumidores, prioridades, requeue e TTL, a ordem deixa de ser garantida de fato, e fluxos que dependem de sequência precisam de desenho próprio, como uma fila por entidade e concorrência controlada por chave. Comparado ao Kafka, o RabbitMQ não oferece ordem por partição com offsets; a ordem é consequência do desenho, não do contrato.

Tipos de fila e escolha operacional

Tipo de fila Característica Quando usar
Clássica Modelo original, réplica sem líder eleito consistente Cargas leves, compatibilidade legada
Quorum Réplica por consenso Raft, failover previsível Dados que não podem se perder em produção
Stream Log appendido com offsets, similar ao Kafka Novos requisitos de replay dentro do RabbitMQ
Lazy Mensagens vão direto ao disco, memória estável Backlogs longos com picos de memória

A direção atual do projeto é clara: filas quorum substituem filas clássicas replicadas, e streams cobrem os casos de log. Times novos devem declarar quorum como padrão para dados importantes, e tratar clássica como exceção de compatibilidade, não como escolha. A migração de clássica para quorum exige recriar a fila e redirecionar bindings, e é planejável com política de consumo duplo durante a troca.

# Topologia de exemplo: pedidos com roteamento topic e DLQ
rabbitmqadmin declare exchange name=pedidos.ex type=topic

rabbitmqadmin declare queue name=pedidos.faturamento \
  durable=true \
  arguments='{"x-dead-letter-exchange":"pedidos.dlx","x-message-ttl":86400000}'

rabbitmqadmin declare queue name=pedidos.dlq durable=true

rabbitmqadmin declare binding source=pedidos.ex \
  destination=pedidos.faturamento routing_key="pedido.*.aprovado"

rabbitmqadmin declare binding source=pedidos.dlx \
  destination=pedidos.dlq routing_key="#"

# Políticas de cluster: fila quorum para durabilidade e DLQ no vhost de produção
rabbitmqctl set_policy filas-duraveis "pedidos\\..*" \
  '{"ha-mode":"exactly","ha-params":2}'

Cenários reais de produção

Fila de jobs de aplicação: envio de notificações, geração de PDF e processamento de imagens são tarefas heterogêneas com picos. RabbitMQ distribui entre workers com prefetch ajustado por peso da tarefa, e a dead letter queue separa o que falhou de vez para o time investigar. É o caso em que RabbitMQ brilha: simplicidade de fila de trabalho com roteamento por tipo de job.

Integração com sistemas legados: um ERP expõe integração frágil e o time precisa amortizar. RabbitMQ recebe eventos do domínio via topic exchange, e filas por sistema legado aplicam o ritmo que o destino aguenta, com TTL e DLQ para o que não couber. O broker vira a fronteira de tolerância entre mundos com velocidades diferentes.

Distribuição de eventos para poucos interessados: um exchange fanout entrega o mesmo evento para três filas de departamentos, e a quarta fila entra amanhã sem tocar no produtor. Para dezenas de consumidores e janela de replay, Kafka seria melhor; para meia dúzia de interessados e fila de trabalho, RabbitMQ entrega o mesmo resultado com operação muito menor.

Quando escolher RabbitMQ

RabbitMQ é a escolha natural para filas de tarefas, roteamento flexível de mensagens, integração entre sistemas com ritmos distintos e cenários de latência baixa por mensagem. Times que precisam de topologia declarativa, como rotear por tipo de evento sem código de despacho, encontram no AMQP a ferramenta certa.

Ele é escolha menos natural para streams com múltiplos grupos independentes lendo o mesmo fluxo com replay, porque não tem modelo de log com offsets, e para volumes que exigem throughput de centenas de milhares de mensagens por segundo sustentados com retenção em disco. Nesses casos Kafka, ou variantes de stream broker, se pagam.

A operação costuma ser o argumento decisivo: clusters com quórum de filas, ferramental maduro de monitoramento por fila, políticas declaradas no broker e um modelo mental simples de fila e ack reduzem a barreira de entrada. O preço aparece em escalas extremas, onde partições, shovels e federations aumentam a complexidade mais rápido do que no modelo de log.

Um contraste útil para a decisão: no RabbitMQ a mensagem é entregue e removida, o que simplifica raciocinar sobre cada item; no Kafka a mensagem permanece e o consumidor avança por offset, o que simplifica reprocessar o fluxo. Fila de trabalho pede o primeiro modelo, stream de eventos pede o segundo, e confundir os dois é a origem de boa parte das arquiteturas de mensageria mal dimensionadas.

Erros comuns e boas práticas

  • Consumir sem ack e sem prefetch: mensagens se perdem na queda do consumidor ou o broker desequilibra a carga; ack manual com prefetch calibrado é o mínimo operacional.
  • Declarar fila não durável para dado que não pode se perder: restart do broker apaga tudo; durabilidade da fila, persistência da mensagem e publisher confirms precisam vir juntos.
  • Requeue infinito de mensagem falha: consumidor com bug transforma a fila em loop; limite de tentativas via header e dead letter exchange resolvem com topologia, não com código.
  • Roteamento complexo demais na topologia: bindings encadeados que ninguém entende viram dívida; documentar a topologia e preferir chaves de assunto bem definidas.
  • Usar RabbitMQ como banco: retenção longa de milhões de mensagens degrada performance; para repositório de eventos com replay, o modelo correto é log particionado.
  • Ignorar filas quorum: filas clássicas replicadas estão em caminho de descontinuação; filas quorum dão consistência de réplica e eleição de líder embutida em clusters.
  • Não monitorar profundidade por fila: cada fila tem seu consumidor e seu SLA; alarme agregado esconde a fila travada atrás da média saudável.
  • Misturar mensagens enormes no fluxo: payload de dezenas de MB bloqueia o pipeline; a prática comum é passar referência, como URL em object storage, e o conteúdo pesado fora do broker.