Mensageria RabbitMQ Kafka Arquitetura Microsserviços

RabbitMQ vs Kafka vs Pulsar: comparação prática para escolher o certo

Entenda as diferenças arquiteturais entre RabbitMQ (message broker), Kafka (event streaming) e Pulsar (all-in-one): modelos de entrega, persistência.

N
Neryx Digital Architects
23 de janeiro de 2026
18 min de leitura
340 profissionais leram
Categoria: Arquitetura Público: Times de engenharia e produto Etapa: Aprendizado

Escolher entre RabbitMQ, Kafka e Pulsar é uma das decisões arquiteturais com maior impacto em sistemas distribuídos. A escolha errada pode limitar a escalabilidade do sistema, adicionar complexidade operacional desnecessária ou criar gargalos que só aparecem em produção, com volume real.

As três ferramentas resolvem o mesmo problema superficialmente — transmitir dados entre produtores e consumidores de forma assíncrona — mas foram projetadas com filosofias completamente diferentes. Este artigo compara cada uma a partir da arquitetura interna, não apenas dos benchmarks, para que você tome uma decisão bem fundamentada.

O que cada uma é, de fato

Antes de comparar, é importante entender o que cada ferramenta se propõe a ser:

  • RabbitMQ é um message broker. Seu papel é rotear mensagens de produtores para consumidores usando regras flexíveis. As mensagens existem para ser processadas e depois descartadas.
  • Kafka é uma plataforma de event streaming. Seu papel é manter um log imutável e ordenado de eventos. Os consumidores leem esse log no ritmo deles — e os eventos podem ser relidos.
  • Pulsar é um sistema all-in-one que combina mensageria e streaming com uma arquitetura de armazenamento separada (BookKeeper). Tenta resolver o que Kafka resolveu, mas com melhor separação entre computação e armazenamento.

Essa diferença de propósito explica a maioria das diferenças práticas que vamos explorar.


RabbitMQ: Message Broker com roteamento inteligente

Arquitetura

No RabbitMQ, o produtor nunca publica mensagens diretamente em filas. Ele publica em um Exchange, que aplica regras de binding para decidir para quais filas a mensagem vai. Esse nível de indireção é o que torna o RabbitMQ extremamente flexível para roteamento.

Existem três tipos principais de Exchange:

  • Direct: roteia para a fila cuja binding key é igual à routing key da mensagem. Uso típico: comandos direcionados a um serviço específico.
  • Topic: roteia usando padrões com wildcards (* para uma palavra, # para várias). Uso típico: eventos categorizados por namespace, como pedido.criado.#.
  • Fanout: ignora routing key e entrega para todas as filas vinculadas. Uso típico: notificações broadcast, invalidação de cache distribuído.

Os consumidores recebem mensagens por push — o broker empurra mensagens assim que elas chegam. Quando múltiplos consumidores estão vinculados à mesma fila (padrão competing consumers), cada mensagem é entregue para apenas um deles, distribuindo a carga automaticamente.

Modelo de persistência

Mensagens no RabbitMQ são transientes por natureza. Depois que um consumidor confirma o recebimento (ack), a mensagem é removida da fila. Você pode configurar filas duráveis e mensagens persistentes para sobreviver a reinicios do broker, mas o modelo mental é: mensagens existem para ser consumidas, não para ser relidas.

Pontos fortes

  • Roteamento flexível e expressivo com exchanges e bindings
  • Suporte nativo a prioridade de mensagens, TTL, dead-letter queues
  • Baixa latência (sub-milissegundo para mensagens pequenas)
  • Dashboard web completo incluído
  • Protocolo AMQP maduro com clients em todas as linguagens
  • Fácil de operar em escala pequena e média

Limitações

  • Não foi projetado para reprocessamento de eventos históricos
  • Escalabilidade horizontal é mais complexa do que no Kafka
  • Throughput menor para volumes muito altos (dezenas de milhares de mensagens/segundo por nó)
  • Sem particionamento nativo como o Kafka

Quando usar RabbitMQ

  • Comunicação assíncrona entre microsserviços com roteamento complexo
  • Filas de trabalho (work queues) com workers que processam tarefas pesadas
  • Sistemas que precisam de RPC assíncrono (request-reply sobre AMQP)
  • Quando a simplicidade operacional é prioridade
  • Ambientes onde o volume de mensagens está na casa de milhares/segundo por tópico

Kafka: Event Streaming com log imutável

Arquitetura

O Kafka armazena eventos em um log distribuído e imutável. Cada tópico é dividido em partições, e cada partição é replicada entre múltiplos brokers para garantir durabilidade e disponibilidade.

Dentro de um cluster, cada partição tem exatamente um broker líder (Leader) e N réplicas (Replica). O produtor escreve sempre no líder (append to leader), e as réplicas se sincronizam em segundo plano. Se o líder falhar, uma réplica é promovida automaticamente.

Os consumidores usam o modelo poll (offset-based) — eles consultam o broker ativamente e controlam qual posição (offset) estão lendo em cada partição. Isso tem uma implicação importante: o Kafka não sabe se uma mensagem foi "processada" — ele só sabe qual offset o consumidor commitou. Isso permite que um consumidor releia todo o histórico de um tópico simplesmente resetando seu offset para zero.

Particionamento e paralelismo

O número de partições de um tópico define o paralelismo máximo de consumo. Em um consumer group, cada consumidor recebe um subconjunto exclusivo de partições. Se você tem 6 partições e 3 consumidores, cada consumidor processa 2 partições em paralelo. Se você tiver mais consumidores do que partições, os consumidores extras ficam ociosos.

Isso também garante ordenação por partition key: todos os eventos com a mesma chave (por exemplo, o mesmo userId) sempre vão para a mesma partição e são processados na ordem em que chegaram.

Pontos fortes

  • Throughput absurdamente alto — milhões de eventos por segundo por cluster
  • Retenção configurável: replique eventos por dias, semanas ou indefinidamente
  • Reprocessamento: consumidores podem reler o histórico completo
  • Ordenação garantida por partição
  • Ecossistema maduro: Kafka Connect, Kafka Streams, ksqlDB
  • Excelente para event sourcing e auditoria imutável

Limitações

  • Operacionalmente complexo: ZooKeeper (até recentemente) ou KRaft, monitoramento, tuning de partições
  • Não foi projetado para roteamento granular de mensagens como o RabbitMQ
  • Latência maior que RabbitMQ para casos de baixo volume (overhead de batching)
  • Armazenamento e computação acoplados no mesmo nó (diferente do Pulsar)
  • Rebalance de partições pode causar pausa temporária no consumo

Quando usar Kafka

  • Event streaming de alto volume: clickstream, logs de aplicação, telemetria IoT
  • Event sourcing — o log do Kafka é o source of truth
  • Pipelines de dados em tempo real com Kafka Streams ou Flink
  • Replicação de dados entre sistemas (CDC com Debezium)
  • Quando você precisa que múltiplos sistemas consumam o mesmo fluxo de eventos de forma independente

Pulsar: All-in-One com armazenamento separado

Arquitetura

O Apache Pulsar separa completamente computação de armazenamento — essa é sua principal inovação arquitetural. O broker Pulsar cuida do roteamento e coordenação, enquanto o Apache BookKeeper cuida da persistência.

O BookKeeper organiza dados em ledgers (segmentos imutáveis). Cada ledger é distribuído entre múltiplos nós chamados Bookies. Quando um segmento é fechado, um novo começa — sem a necessidade de rebalancear partições inteiras como no Kafka.

O consumo é cursor-based via modelo de subscription. O Pulsar suporta 4 tipos de subscription:

  • Exclusive: apenas um consumidor por subscription
  • Shared: múltiplos consumidores, cada mensagem vai para um deles (como RabbitMQ)
  • Failover: um consumidor ativo, outro em standby
  • Key_Shared: mensagens com a mesma chave sempre vão para o mesmo consumidor (como Kafka)

Essa flexibilidade de subscription faz com que o Pulsar consiga emular tanto o comportamento do RabbitMQ quanto o do Kafka, dependendo da configuração.

Pontos fortes

  • Separação entre broker e armazenamento simplifica escala horizontal de cada camada
  • Multi-tenancy nativo com namespaces e políticas de isolamento
  • Suporte a mensageria E streaming no mesmo sistema
  • Tiered storage: mover dados antigos automaticamente para S3 ou GCS
  • Geo-replicação nativa entre regiões
  • Sem rebalance de partições (substituição de ledgers é transparente)

Limitações

  • Operacionalmente mais complexo que Kafka: gerenciar Pulsar brokers + BookKeeper + ZooKeeper
  • Ecossistema menor que o Kafka (menos conectores, ferramentas e material de referência)
  • Curva de aprendizado mais íngreme
  • Adoção ainda menor no mercado — menos profissionais com experiência

Quando usar Pulsar

  • Quando você precisa de mensageria e streaming no mesmo sistema, sem operar dois clusters
  • Multi-tenancy rigoroso: SaaS ou ambientes com múltiplos times compartilhando infraestrutura
  • Geo-replicação entre data centers como requisito nativo
  • Tiered storage: volume massivo com custo controlado (dados quentes em SSD, frios no S3)
  • Times com experiência em Kafka que querem resolver limitações operacionais

Comparação direta: tabela de decisão

Critério RabbitMQ Kafka Pulsar
Modelo de entrega Push (broker → consumer) Poll (consumer → broker, offset) Push via subscription (cursor-based)
Retenção de mensagens Descartada após ack Configurável (dias/semanas/forever) Configurável + tiered storage no S3
Reprocessamento Não nativo Sim, por reset de offset Sim, por reset de cursor
Ordenação Por fila (FIFO) Por partição Por partição (Key_Shared)
Throughput Médio (~100k msg/s por nó) Muito alto (~1M+ msg/s por cluster) Alto, comparável ao Kafka
Latência Muito baixa (sub-ms) Baixa (batching pode adicionar ms) Baixa
Roteamento flexível ✅ Exchanges/bindings/wildcards ❌ Apenas tópico ⚠️ Básico
Multi-tenancy nativo ⚠️ Básico (vhosts) ❌ Não nativo ✅ Namespaces + políticas
Geo-replicação ⚠️ Federation plugin ⚠️ MirrorMaker 2 ✅ Nativo
Complexidade operacional Baixa Média-alta Alta
Ecossistema Maduro Muito maduro Em crescimento

Uso com .NET na prática

RabbitMQ no .NET

A combinação mais recomendada é MassTransit + RabbitMQ. O MassTransit abstrai o broker, cuida de serialização, retry automático, dead-letter queues e registro de consumers sem boilerplate:

// Registrar publisher e consumer com MassTransit
builder.Services.AddMassTransit(x =>
{
    x.AddConsumer<PedidoCriadoConsumer>();

    x.UsingRabbitMq((context, cfg) =>
    {
        cfg.Host("rabbitmq://localhost", h =>
        {
            h.Username("guest");
            h.Password("guest");
        });

        // Exchange tipo Topic → fila vinculada automaticamente
        cfg.ReceiveEndpoint("pedidos-criados", e =>
        {
            e.ConfigureConsumer<PedidoCriadoConsumer>(context);
        });
    });
});

Kafka no .NET

Para Kafka, as principais opções são Confluent.Kafka (baixo nível, máxima performance) ou MassTransit com transport Kafka (consistência de modelo se você já usa MassTransit). Para pipelines mais complexos, considere Streamiz (Kafka Streams em .NET):

// Producer com Confluent.Kafka
var config = new ProducerConfig { BootstrapServers = "localhost:9092" };
using var producer = new ProducerBuilder<string, string>(config).Build();

await producer.ProduceAsync("pedidos", new Message<string, string>
{
    Key = pedido.Id.ToString(),   // garante ordenação por pedido
    Value = JsonSerializer.Serialize(pedido)
});

// Consumer com commit manual de offset
var consumerConfig = new ConsumerConfig
{
    BootstrapServers = "localhost:9092",
    GroupId = "servico-notificacoes",
    AutoOffsetReset = AutoOffsetReset.Earliest,
    EnableAutoCommit = false   // commit manual após processar
};

using var consumer = new ConsumerBuilder<string, string>(consumerConfig).Build();
consumer.Subscribe("pedidos");

while (!cancellationToken.IsCancellationRequested)
{
    var result = consumer.Consume(cancellationToken);
    await ProcessarPedidoAsync(result.Message.Value);
    consumer.Commit(result);  // só commita após processar com sucesso
}

Pulsar no .NET

O client oficial é o DotPulsar (open source, mantido pela Apache Foundation). A API é assíncrona e moderna, porém o ecossistema de abstrações de alto nível é mais escasso que o MassTransit para RabbitMQ/Kafka:

await using var client = PulsarClient.Builder()
    .ServiceUrl(new Uri("pulsar://localhost:6650"))
    .Build();

await using var producer = client.NewProducer(Schema.String)
    .Topic("persistent://tenant/namespace/pedidos")
    .Create();

await producer.Send(JsonSerializer.Serialize(pedido));

// Consumer com subscription Shared (concorrente como RabbitMQ)
await using var consumer = client.NewConsumer(Schema.String)
    .Topic("persistent://tenant/namespace/pedidos")
    .SubscriptionName("servico-notificacoes")
    .SubscriptionType(SubscriptionType.Shared)
    .Create();

await foreach (var message in consumer.Messages())
{
    await ProcessarPedidoAsync(message.Value());
    await consumer.Acknowledge(message);
}

Armadilhas comuns em cada tecnologia

RabbitMQ

  • Filas não limitadas acumulando mensagens: configure x-max-length ou use políticas de overflow. Uma fila com milhões de mensagens degrada o nó inteiro.
  • Prefetch ilimitado: o padrão de prefetch pode fazer um consumer alocar milhares de mensagens em memória antes de processar. Configure BasicQos(prefetchCount: 1) para garantir distribuição uniforme.
  • Consumir sem ack manual: se o consumer cair antes de ackear, a mensagem retorna à fila. Confirme sempre depois do processamento, não antes.

Kafka

  • Número insuficiente de partições: você não pode escalar além do número de partições. Planejar o número inicial de partições é crítico — adicionar depois causa rebalance.
  • Consumer group rebalance durante deploys: implemente IConsumerRebalanceListener para commitar offsets antes do rebalance, evitando reprocessamento desnecessário.
  • Mensagens grandes: Kafka é otimizado para mensagens pequenas a médias. Para payloads maiores, armazene no S3 e passe apenas a referência no evento (padrão Claim Check).

Pulsar

  • Subestimar a complexidade do BookKeeper: tuning de Bookies (número de journals, ledger directories) tem impacto direto na performance. Não negligencie o monitoramento do BookKeeper.
  • Misturar tipos de subscription: exclusive e shared na mesma subscription têm comportamentos muito diferentes. Defina o tipo de subscription explicitamente e documente a razão.

Como escolher: árvore de decisão

Use este fluxo para guiar a decisão:

  1. Você precisa reler eventos históricos ou processar streams em tempo real?
    → Sim: Kafka ou Pulsar. Vá para o passo 2.
    → Não: RabbitMQ é a escolha natural.
  2. Você precisa de multi-tenancy nativo ou geo-replicação sem plugins?
    → Sim: Pulsar.
    → Não: Kafka.
  3. Sua equipe tem experiência com Kafka?
    → Sim: fique no Kafka, a menos que as limitações do passo 2 se apliquem.
    → Não: avalie se o investimento em Pulsar se justifica para o problema.
  4. Você está construindo um SaaS com múltiplos clientes compartilhando infraestrutura?
    → Considere Pulsar pelos namespaces e políticas de isolamento nativas.
    → Do contrário, RabbitMQ (baixo volume) ou Kafka (alto volume) são mais simples de operar.

Conclusão

RabbitMQ, Kafka e Pulsar não são substituíveis um pelo outro sem consequências arquiteturais. Eles foram projetados para resolver problemas diferentes com prioridades diferentes:

  • Use RabbitMQ quando precisar de roteamento flexível, baixa latência e simplicidade operacional. É a escolha padrão para comunicação assíncrona entre microsserviços na maioria dos sistemas de médio porte.
  • Use Kafka quando precisar de throughput massivo, retenção de eventos e capacidade de reprocessar o histórico. É a espinha dorsal de plataformas de dados, pipelines de telemetria e event sourcing em larga escala.
  • Use Pulsar quando precisar do melhor dos dois mundos em um único cluster, com multi-tenancy robusto e geo-replicação nativa — aceitando a complexidade operacional adicional.

Na prática, a maioria dos sistemas de médio porte começa com RabbitMQ e migra partes específicas para Kafka quando o volume justifica. Coexistência dos dois é totalmente comum e muitas vezes a abordagem mais pragmática.

Independente da escolha, o padrão Outbox é recomendado para garantir consistência entre banco de dados e mensageria — evita mensagens perdidas em caso de falha no meio do processamento. Se você usa MassTransit, o suporte a Outbox está embutido e funciona com RabbitMQ e Kafka sem mudança de código.

Precisa desenhar a próxima fase com menos retrabalho?

Fazemos discovery técnico para mapear riscos, arquitetura-alvo e sequência de execução antes de investir pesado.

Solicitar Discovery

Newsletter

Receba artigos como este no seu e-mail

Conteúdo técnico sobre arquitetura de software, .NET, IA e gestão de produto. Sem spam.