SYSTEM DESIGN · MENSAGERIA · LEITURA DE ~13 MIN
Filas e Mensageria
Toda fila de back pressure precisa de um motor por baixo. Redis, RabbitMQ, Kafka, Celery e BullMQ resolvem o mesmo problema de fundo — segurar trabalho e desacoplar sistemas —, mas cada um do seu jeito.
SEÇÃO 01
Por que existe fila e worker
No artigo sobre back pressure, a fila aparece como uma peça abstrata: um espaço limitado entre a API e os workers, que impede que a entrada destrua um componente mais lento na saída. Mas "fila" não é uma coisa só. Na prática, ela é implementada por ferramentas diferentes, e cada uma resolve uma parte diferente do problema.
Producer
↓
Broker / Fila
↓
Consumer / Worker
Redis guarda estrutura de dados rápida em memória. RabbitMQ roteia mensagens com garantia de entrega. Kafka mantém um log de eventos que vários consumidores podem ler de forma independente. Celery e BullMQ são bibliotecas que rodam tasks em background usando um desses brokers por baixo.
Este artigo passa por cada uma dessas peças e termina mostrando onde exatamente o back pressure entra quando elas trabalham juntas.
SEÇÃO 02
Redis: a peça multiuso
Redis é uma estrutura de dados em memória. A forma mais simples de pensar nele: um dicionário gigante, muito rápido, que também sabe guardar listas, sets, hashes e streams — não só pares chave-valor.
Por causa dessa versatilidade, Redis vira base para várias coisas diferentes: cache (ver estratégias de cache), sessões de usuário, rate limiting, locks distribuídos e, com uma lista ou um Stream, uma fila simples.
LPUSH fila:emails "{ \"to\": \"a@x.com\" }"
BRPOP fila:emails 0
Uma lista Redis não tem ACK, retry automático nem dead-letter queue: se o worker cair depois do BRPOP e antes de terminar o processamento, a mensagem some. É por isso que ferramentas como BullMQ existem — elas usam Redis como armazenamento, mas adicionam a camada de garantias em cima.
SEÇÃO 03
RabbitMQ: broker de mensagens
RabbitMQ é um message broker dedicado: um servidor cuja única função é receber, rotear e entregar mensagens entre quem produz e quem consome, com confirmação de entrega.
Producer
↓
Exchange (roteia por regra)
↓
Queue
↓
Consumer → ACK
A peça extra em relação a uma fila Redis é o ACK explícito: o consumer só confirma a mensagem depois de terminar de processá-la. Se o consumer cair no meio, o RabbitMQ redelivera a mensagem para outro worker — nada se perde por causa de um crash isolado.
O exchange é quem decide para qual fila cada mensagem vai. Um exchange direct roteia por uma chave exata; um topic roteia por padrão (pedido.*); um fanout copia a mensagem para todas as filas ligadas a ele. Mensagens que falham repetidamente podem cair numa dead-letter queue em vez de tentar para sempre.
SEÇÃO 04
Kafka: streaming de eventos
Kafka resolve um problema diferente do RabbitMQ. Em vez de uma fila que esvazia conforme é consumida, Kafka mantém um log de eventos: cada mensagem publicada em um tópico fica retida por um tempo configurável, e qualquer consumer pode lê-la — inclusive mais de uma vez.
Producer
↓
Topic (partições)
↓
Consumer A · Consumer B · Consumer C
(cada um lê independente)
Isso muda o que é possível fazer: um evento payment.completed pode ser lido ao mesmo tempo por um serviço de analytics, um serviço de auditoria e um serviço de recomendação, sem que um dependa do outro nem "roube" a mensagem dos demais.
No RabbitMQ, a mensagem some da fila depois do ACK. No Kafka, o evento continua no log até o tempo de retenção expirar — o que permite replay: reprocessar os últimos N eventos, ou ligar um consumer novo que lê tudo desde o início.
SEÇÃO 05
Celery: task queue em Python
Celery é um framework Python para rodar tasks em background usando workers. Ele não é um broker — ele precisa de um (normalmente Redis ou RabbitMQ) para guardar as tasks pendentes.
@app.task(bind=True, max_retries=3)
def send_email(self, user_id):
try:
email_service.send(user_id)
except Exception as exc:
raise self.retry(exc=exc, countdown=10)
# na API:
send_email.delay(user_id)
A chamada .delay(...) publica a task no broker e retorna imediatamente. Um worker Celery, rodando separado da API, pega a task e executa. Retries, agendamento periódico (Celery beat) e jobs longos fazem parte do que o framework já resolve pronto.
SEÇÃO 06
BullMQ: task queue em Node.js
BullMQ cumpre o mesmo papel do Celery, mas para o ecossistema Node/TypeScript, e é construído diretamente sobre Redis.
const queue = new Queue("notifications", { connection: redis });
await queue.add("push", { userId }, { attempts: 3 });
new Worker("notifications", async (job) => {
await pushService.send(job.data.userId);
}, { connection: redis, concurrency: 20 });
O concurrency ali não é detalhe: é exatamente o tipo de limite discutido em back pressure — quantos jobs esse worker processa ao mesmo tempo. Se 5000 notificações entrarem de uma vez, a fila cresce, mas o worker continua consumindo no ritmo que ele suporta, em vez de tentar tudo junto.
SEÇÃO 07
Task x evento: comando ou fato?
Com as cinco ferramentas na mesa, fica mais fácil ver uma distinção que confunde bastante gente: task e evento não são sinônimos.
Uma task é uma instrução de trabalho — "faça isso": send_email, generate_teams, resize_image. Normalmente um único worker executa e o resultado importa para quem pediu. Um evento é um fato que já aconteceu — "isso aconteceu": payment.completed, match.cancelled. Vários consumidores podem reagir a ele de forma independente, e nenhum "dono" precisa saber quem está ouvindo.
Task → fila de jobs (BullMQ, Celery, RabbitMQ). Evento → stream / event bus (Kafka). Misturar os dois é comum no começo: usar Kafka para mandar "gere esse PDF" ou usar uma fila de jobs para avisar dez serviços diferentes que um pagamento foi confirmado. Os dois funcionam torto quando usados no papel do outro.
SEÇÃO 08
Tudo junto: um mini e-commerce
Colocando as peças lado a lado num sistema só, fica mais claro onde cada uma entra.
Criar um pedido é síncrono: a API consulta o PostgreSQL, confirma o estoque e responde — o mesmo caminho de consistência descrito em back pressure. Notificar o cliente, gerar a fatura em PDF e atualizar o cache são async: viram jobs numa fila BullMQ/Redis, processados por workers dedicados. Analytics, auditoria e recomendações não pedem uma resposta imediata a ninguém: são consumers independentes de um evento no Kafka.
SEÇÃO 09
Ideia central
Nenhuma dessas ferramentas substitui a disciplina de back pressure — elas só dão o material com que ela é construída.
Internet
↓
Rate Limit
↓
Concurrency Limit
↓
Connection Pool
↓
Bounded Queue (RabbitMQ / BullMQ / Celery)
↓
Workers
↓
PostgreSQL · Kafka (eventos) · Redis (cache)
Trocar Redis por RabbitMQ, ou RabbitMQ por Kafka, não elimina a pergunta feita em back pressure: o que acontece quando entra mais trabalho do que o sistema consegue escoar? Cada ferramenta muda a resposta técnica — tamanho de fila, ACK, retenção, replay —, mas a decisão de arquitetura continua sendo a mesma: aceitar até onde dá para operar bem, e deixar claro o que acontece com o excesso.
FIM · OBRIGADO POR LER
A fila é só a interface. O que importa é o contrato por trás dela.
Redis, RabbitMQ, Kafka, Celery e BullMQ resolvem partes diferentes do mesmo problema: como aceitar trabalho sem derrubar quem vem depois.