DevGuideTK
← Trilha Backend

Filas e mensageria

RabbitMQ, Kafka e o que muda entre uma fila de tarefas e um log de eventos.

As decisões de mensageria são quatro: qual intermediário usar, como tratar mensagens que falham, como garantir a ordem quando ela importa e como publicar sem perder mensagens.

Os intermediários

RabbitMQKafkaServiço gerenciado (SQS, Pub/Sub, Service Bus)
ModeloFila de tarefas, com roteamento flexívelLog de eventos particionadoFila (SQS, Service Bus) ou publicação e assinatura (Pub/Sub, SNS)
Mensagem depois de processadaRemovidaGuardada pelo tempo configuradoRemovida
Reler o passadoNão (salvo com streams do RabbitMQ)SimNão, ou de forma limitada
OrdemPor fila, com um consumidorPor partiçãoEm filas FIFO, por grupo de mensagens
VolumeDezenas de milhares de mensagens por segundoCentenas de milhares a milhões por segundoEscala automática
OperaçãoModeradaAlta, ou um serviço gerenciadoNenhuma

RabbitMQ

Forte em distribuir trabalho. Oferece roteamento por tipo de mensagem, prioridades, mensagens com atraso, filas de mensagens mortas e confirmação por mensagem. É a escolha natural para tarefas em segundo plano: e-mails, processamento de arquivos, integrações.

Kafka

Forte em fluxos de eventos com alto volume e vários consumidores. Como guarda as mensagens, permite que um serviço novo leia todo o histórico e que dados sejam reprocessados. É a base de arquiteturas orientadas a eventos e de processamento de streams. O custo é a complexidade: partições, grupos de consumidores e retenção exigem planejamento, e operar um cluster próprio é trabalho especializado.

Serviços gerenciados

Para quem já está numa nuvem, SQS, Pub/Sub e Service Bus eliminam a operação e escalam sozinhos. As limitações estão nos detalhes: tamanho máximo de mensagem, tempo máximo de retenção, garantias de ordem só em modos específicos e custo por mensagem.

O banco que você já tem

Para volumes modestos, uma tabela no PostgreSQL funciona como fila, com SELECT ... FOR UPDATE SKIP LOCKED: cada consumidor pega uma linha que ninguém está processando. Bibliotecas como Graphile Worker, Oban, River e good_job seguem esse modelo. A vantagem é grande: a mensagem é gravada na mesma transação dos dados, o que elimina o problema de publicar sem perder. O limite é o volume e a carga extra no banco.

Como escolher

SituaçãoEscolha
Tarefas em segundo plano num sistema pequeno ou médioFila no próprio banco, ou RabbitMQ
Distribuir trabalho entre consumidores, com novas tentativas e atrasosRabbitMQ ou SQS
Vários serviços reagindo aos mesmos eventosKafka, ou publicação e assinatura gerenciada
Reprocessar eventos passados, ou alimentar análisesKafka
Volume muito alto de eventos, como telemetria e cliquesKafka
Já está numa nuvem e não quer operar nadaO serviço gerenciado dela

Quando uma mensagem falha

Uma mensagem pode falhar por dois motivos diferentes, e cada um pede um tratamento:

Tipo de falhaExemploO que fazer
TemporáriaO serviço de e-mail está fora do ar, o banco recusou a conexãoTentar de novo, mais tarde
PermanenteJSON inválido, pedido que não existe, regra de negócio violadaTentar de novo não adianta; separar a mensagem para análise

Novas tentativas com atraso

Devolver a mensagem para a fila imediatamente faz o consumidor tentar de novo em milissegundos, enquanto o serviço externo ainda está fora do ar. As novas tentativas precisam de atraso crescente: 10 segundos, 1 minuto, 10 minutos.

Cada intermediário oferece isso de um jeito: filas de espera com tempo de expiração ou o recurso de mensagens atrasadas no RabbitMQ, o tempo de visibilidade no SQS, tópicos de nova tentativa no Kafka.

A fila de mensagens mortas

Depois de algumas tentativas, ou numa falha permanente, a mensagem vai para uma fila de mensagens mortas (dead letter queue). Ela sai do caminho das outras, fica guardada para análise e pode ser reenviada depois que o problema for corrigido.

Ordem por chave

Quando a ordem importa, como os eventos "pedido criado", "pedido pago" e "pedido cancelado" de um mesmo pedido, ela é garantida por chave:

IntermediárioComo garantir a ordem por chave
KafkaA chave da mensagem (por exemplo, o id do pedido) define a partição; uma partição é lida por um consumidor do grupo de cada vez
SQS FIFOO MessageGroupId agrupa as mensagens; cada grupo é entregue em ordem
RabbitMQUma fila por chave, ou o plugin de hash consistente para dividir por chave

Mesmo com ordem por chave, prefira consumidores que tolerem desordem: guardar a versão ou a data do evento e ignorar um evento mais antigo que o estado atual. Novas tentativas e reprocessamentos sempre podem embaralhar a sequência.

Publicar sem perder mensagens

O problema mais sutil fica no produtor:

1. grava o pedido no banco       ✔
2. publica "pedido criado"       ✘  o intermediário estava fora do ar

O pedido existe, e nenhum outro serviço sabe dele. Inverter a ordem não resolve: a mensagem pode ser publicada e a gravação no banco falhar.

A solução é o padrão outbox: a mensagem é gravada numa tabela do próprio banco, na mesma transação do pedido. Um processo separado lê essa tabela e publica as mensagens, marcando cada uma como enviada. Se a publicação falhar, ela é tentada de novo. Como esse processo também pode publicar a mesma mensagem duas vezes, os consumidores continuam precisando ser idempotentes. O padrão tem um tópico próprio na trilha.

Quando não usar uma fila

SituaçãoPor quê
O usuário precisa da resposta para continuarA fila só adia; ele vai esperar do mesmo jeito, com mais complexidade
Volume baixo e uma única chamada simplesUma chamada síncrona com tempo limite e nova tentativa resolve
Ninguém vai monitorar a filaMensagens mortas e atrasos acumulam sem ninguém ver

Resumo da decisão

  1. Intermediário: fila no banco ou RabbitMQ para tarefas; Kafka para fluxos de eventos com vários consumidores; gerenciado se já estiver numa nuvem.
  2. Confirmação: só depois de processar.
  3. Idempotência: todo consumidor tolera a mesma mensagem duas vezes.
  4. Falhas: novas tentativas com atraso, limite de tentativas e fila de mensagens mortas monitorada.
  5. Ordem: por chave, e consumidores que toleram desordem.
  6. Publicação: padrão outbox para não perder mensagens.

O próximo nível mostra um consumidor do RabbitMQ nas cinco linguagens e a idempotência com o banco.

Prompts para aprofundar

Ao abrir no ChatGPT ou no Claude, o prompt é enviado ao serviço escolhido pela URL e também fica copiado na sua área de transferência.

  • Escolher o intermediário de mensagens

    Preciso escolher um intermediário de mensagens para o meu sistema.
    
    Faça estas perguntas, uma por vez, e espere as respostas:
    - Que tipo de trabalho vai passar pelas filas: tarefas em segundo plano, eventos para vários serviços, ou os dois?
    - Qual é o volume esperado de mensagens por segundo?
    - Alguém vai precisar reler mensagens antigas?
    - A ordem das mensagens importa? Em que escopo?
    - O sistema está numa nuvem? A equipe tem experiência em operar algum intermediário?
    
    Com as respostas, compare para o meu caso uma fila no próprio banco de dados, o RabbitMQ, o Kafka e o serviço gerenciado da minha nuvem. Seja concreto sobre o que cada opção resolve e o que custa operar.
    
    Termine com a recomendação, os riscos dela e os sinais que indicariam a necessidade de mudar no futuro.
  • Tratar falhas, novas tentativas e mensagens mortas

    Quero desenhar o tratamento de falhas dos meus consumidores de mensagens.
    
    Pergunte qual intermediário eu uso.
    
    Explique, para ele:
    1. A diferença entre falha temporária e permanente, com exemplos, e como o consumidor distingue as duas.
    2. Por que devolver a mensagem para a fila imediatamente é um problema.
    3. Como implementar novas tentativas com atraso crescente no meu intermediário.
    4. Como configurar o limite de tentativas e a fila de mensagens mortas.
    5. O que é uma mensagem envenenada e como ela trava uma fila com ordem garantida.
    6. Como monitorar a fila de mensagens mortas e reenviar as mensagens depois de corrigir o problema.
    
    Termine com o desenho completo, com as filas, os atrasos e os alertas.
  • Publicar sem perder mensagens

    Quero entender por que gravar no banco e depois publicar uma mensagem pode perder dados, e como resolver.
    
    Mostre com linhas do tempo o que dá errado nas duas ordens: gravar e depois publicar, e publicar e depois gravar.
    
    Depois explique o padrão outbox:
    1. A tabela de saída e o que vai nela.
    2. Por que a mensagem é gravada na mesma transação dos dados.
    3. Como o processo de publicação lê e marca as mensagens, e o que acontece se ele cair.
    4. Por que os consumidores continuam precisando ser idempotentes.
    5. As alternativas de leitura: consultar a tabela periodicamente ou capturar mudanças do banco (CDC).
    
    Pergunte qual banco e qual intermediário eu uso e mostre como ficaria no meu caso.