---
title: "Python com Kafka e RabbitMQ: Mensageria na Prática"
url: "https://python.dev.br/blog/python-kafka-rabbitmq-mensageria/"
markdown_url: "https://python.dev.br/blog/python-kafka-rabbitmq-mensageria.MD"
description: "Entenda quando usar Kafka, RabbitMQ, Redis Streams ou filas simples em projetos Python, com exemplos práticos, arquitetura, retries, idempotência e portfólio."
date: "2026-06-04"
author: "Equipe Python Brasil"
---

# Python com Kafka e RabbitMQ: Mensageria na Prática

Entenda quando usar Kafka, RabbitMQ, Redis Streams ou filas simples em projetos Python, com exemplos práticos, arquitetura, retries, idempotência e portfólio.


Mensageria aparece cada vez mais em vagas Python no Brasil porque sistemas reais raramente fazem tudo dentro de uma única requisição HTTP. Uma API recebe um pedido, publica um evento, outro serviço envia email, um worker atualiza o estoque, um pipeline alimenta analytics e um processo separado recalcula score de risco. Sem uma fila ou broker de mensagens, esse fluxo vira uma sequência frágil de chamadas síncronas: se uma integração cai, a experiência do usuário fica lenta ou a operação perde dados.

Python combina bem com esse tipo de arquitetura. A linguagem é usada em APIs, automações, pipelines de dados, integrações de CRM, tarefas assíncronas, machine learning e sistemas internos. O ponto não é decorar uma ferramenta específica, e sim entender quando usar uma fila simples, quando usar RabbitMQ, quando Kafka faz sentido e quais cuidados evitam duplicidade, perda de mensagem e debug impossível.

Neste guia, vamos comparar Kafka, RabbitMQ, Redis Streams e filas simples para projetos Python. Também veremos exemplos com `pika` e `confluent-kafka`, desenho de arquitetura, padrões de retry, idempotência, observabilidade e ideias de portfólio. Se sua dúvida é tirar trabalho pesado de uma API FastAPI, leia também [FastAPI Background Tasks, Celery e Redis](/blog/fastapi-background-tasks-celery-redis-2026/), [Python e Redis](/blog/python-e-redis/), [Docker Compose com PostgreSQL](/blog/python-docker-compose-postgres-ambiente-local/) e [observabilidade com OpenTelemetry em Python](/blog/opentelemetry-python-observabilidade/).

## O que é mensageria

Mensageria é a prática de enviar dados entre partes de um sistema usando mensagens. Em vez de um serviço chamar outro diretamente e esperar resposta imediata, ele publica uma mensagem em um intermediário. Esse intermediário pode ser RabbitMQ, Kafka, Redis Streams, SQS, Pub/Sub, NATS ou outro broker. Um consumidor lê a mensagem e executa o trabalho quando estiver pronto.

Um exemplo simples:

1. a API recebe um cadastro;
2. salva o usuário no banco;
3. publica `usuario_criado` em uma fila ou tópico;
4. um worker envia email de boas-vindas;
5. outro worker registra o evento para analytics;
6. outro processo atualiza uma ferramenta de CRM.

Essa separação reduz acoplamento. A API não precisa conhecer todos os detalhes de email, analytics e CRM. Ela registra o fato importante e deixa consumidores especializados reagirem. O fluxo também melhora resiliência: se o serviço de email ficar fora do ar, a mensagem pode esperar retry sem derrubar o cadastro.

## Fila, tópico e evento não são a mesma coisa

Em conversas de arquitetura, muita gente chama tudo de fila. Isso confunde decisões.

| Conceito | Ideia central | Exemplo |
|---|---|---|
| Fila | uma mensagem normalmente é processada por um consumidor do grupo | processar PDFs enviados por usuários |
| Tópico | vários consumidores podem receber o mesmo evento | `pedido_pago` consumido por estoque, nota fiscal e analytics |
| Evento | registro de algo que aconteceu no domínio | `curriculo_enviado`, `pagamento_confirmado` |
| Comando | pedido para alguém executar uma ação | `enviar_email_confirmacao` |

RabbitMQ costuma ser excelente para filas, roteamento e comandos assíncronos. Kafka brilha quando você tem tópicos com histórico, alto volume, múltiplos consumidores e necessidade de replay. Redis Streams pode ser uma alternativa intermediária quando você já usa Redis e precisa de grupos de consumidores com persistência básica. Uma tabela no banco com status também pode ser suficiente para produtos pequenos.

## Quando usar RabbitMQ

RabbitMQ é uma escolha comum para tarefas assíncronas, workflows operacionais e comunicação entre serviços quando você quer filas confiáveis e roteamento flexível. Ele trabalha com exchanges, queues, bindings e acknowledgements.

Use RabbitMQ quando:

1. cada mensagem deve ser processada por um worker;
2. você precisa de filas por prioridade, tipo de tarefa ou time;
3. retry e dead letter queue são importantes;
4. o volume é moderado e o fluxo é mais transacional;
5. você quer roteamento por chave, como `email.confirmacao` ou `pagamento.falha`.

Um exemplo de produtor com `pika`:

```python
import json

import pika


def publicar_email_confirmacao(user_id: str, email: str) -> None:
    connection = pika.BlockingConnection(pika.URLParameters("amqp://guest:guest@localhost:5672/"))
    channel = connection.channel()
    channel.queue_declare(queue="emails", durable=True)

    mensagem = {"tipo": "email_confirmacao", "user_id": user_id, "email": email}
    channel.basic_publish(
        exchange="",
        routing_key="emails",
        body=json.dumps(mensagem).encode("utf-8"),
        properties=pika.BasicProperties(delivery_mode=2),
    )
    connection.close()
```

O `delivery_mode=2` pede persistência da mensagem. Isso não elimina todos os riscos, mas evita tratar a fila como memória temporária. Em produção, você também configuraria usuário próprio, TLS, heartbeat, retry de conexão e observabilidade.

O consumidor básico:

```python
import json

import pika


def processar(ch, method, properties, body):
    mensagem = json.loads(body)
    enviar_email(mensagem["email"], mensagem["user_id"])
    ch.basic_ack(delivery_tag=method.delivery_tag)


connection = pika.BlockingConnection(pika.URLParameters("amqp://guest:guest@localhost:5672/"))
channel = connection.channel()
channel.queue_declare(queue="emails", durable=True)
channel.basic_qos(prefetch_count=10)
channel.basic_consume(queue="emails", on_message_callback=processar)
channel.start_consuming()
```

O `basic_ack` só deve acontecer depois do trabalho importante terminar. Se você confirmar antes e o worker cair no meio, a mensagem pode ser perdida. Se você nunca confirmar, a mensagem pode voltar para a fila e ser processada novamente. Por isso idempotência é obrigatória.

## Quando usar Kafka

Kafka é mais do que uma fila. Ele funciona como um log distribuído de eventos. Produtores publicam mensagens em tópicos, consumidores leem em grupos, offsets indicam até onde cada grupo avançou e mensagens podem ser retidas por tempo ou tamanho. Isso permite replay: um novo consumidor pode reler eventos antigos para reconstruir estado, alimentar analytics ou recalcular uma projeção.

Use Kafka quando:

1. o mesmo evento precisa alimentar vários consumidores independentes;
2. você precisa de histórico e replay;
3. o volume de eventos é alto;
4. pipelines de dados e analytics fazem parte do produto;
5. a ordem por chave, como `pedido_id`, importa;
6. você aceita operar uma ferramenta mais complexa.

Um produtor com `confluent-kafka`:

```python
import json

from confluent_kafka import Producer


producer = Producer({"bootstrap.servers": "localhost:9092"})


def publicar_pedido_pago(pedido_id: str, valor: float) -> None:
    evento = {"tipo": "pedido_pago", "pedido_id": pedido_id, "valor": valor}
    producer.produce(
        "pedidos",
        key=pedido_id,
        value=json.dumps(evento).encode("utf-8"),
    )
    producer.flush()
```

A chave (`key`) ajuda Kafka a enviar eventos do mesmo pedido para a mesma partição, preservando ordem relativa dentro daquela chave. Isso é útil para fluxos como `pedido_criado`, `pedido_pago`, `pedido_cancelado`.

Um consumidor:

```python
import json

from confluent_kafka import Consumer


consumer = Consumer(
    {
        "bootstrap.servers": "localhost:9092",
        "group.id": "analytics-pedidos",
        "auto.offset.reset": "earliest",
        "enable.auto.commit": False,
    }
)
consumer.subscribe(["pedidos"])

while True:
    msg = consumer.poll(1.0)
    if msg is None:
        continue
    if msg.error():
        raise RuntimeError(msg.error())

    evento = json.loads(msg.value())
    salvar_evento_analytics(evento)
    consumer.commit(msg)
```

Desabilitar commit automático deixa claro quando o offset avança: depois que o processamento relevante terminou. Ainda assim, consumidores Kafka também precisam ser idempotentes. Em falhas, a mesma mensagem pode ser lida mais de uma vez.

## Redis Streams e filas simples

Redis aparece bastante em projetos Python por causa de cache, rate limit e filas simples. Para filas muito básicas, listas com `LPUSH` e `BRPOP` resolvem protótipos. Para algo mais estruturado, Redis Streams oferece stream persistente, consumer groups e acknowledgements.

Redis Streams pode fazer sentido quando:

1. você já opera Redis;
2. o volume é moderado;
3. precisa de grupos de consumidores;
4. quer menos complexidade operacional que Kafka;
5. aceita limitações de retenção, replay e ecossistema.

Mas cuidado: Redis não deve virar banco principal disfarçado. Se a mensagem representa dinheiro, contrato, estoque ou outro fato crítico, grave também em banco transacional ou use um broker com estratégia operacional clara.

Para casos mínimos, uma tabela `jobs` no PostgreSQL pode ser melhor que introduzir broker cedo demais. A API insere um job com `status = 'pendente'`, um worker busca pendentes com lock, processa, atualiza status e registra erro. É menos elegante, mas muito auditável e suficiente para muitos produtos internos.

## Comparação rápida

| Ferramenta | Melhor uso | Custo operacional | Replay | Observação |
|---|---|---:|---:|---|
| BackgroundTasks | tarefas leves no mesmo processo | baixo | não | bom para notificações não críticas |
| Celery + Redis/RabbitMQ | tarefas assíncronas em workers | médio | limitado | ótimo para web apps Python |
| RabbitMQ | filas e roteamento transacional | médio | limitado | forte para comandos e retry |
| Kafka | eventos, streams e analytics | alto | sim | forte para escala e múltiplos consumidores |
| Redis Streams | fila persistente intermediária | médio | parcial | útil se Redis já existe |
| Tabela no banco | jobs auditáveis simples | baixo | por consulta | subestimada para produtos pequenos |

Não escolha Kafka só porque parece mais sênior. Se o produto tem dez tarefas por minuto e um único consumidor, RabbitMQ, Celery ou uma tabela de jobs podem ser mais saudáveis. Use Kafka quando o problema realmente pede log de eventos, múltiplos consumidores, retenção e escala.

## Idempotência: o detalhe que separa demo de produção

Em mensageria, assuma que uma mensagem pode ser processada mais de uma vez. Rede cai, worker reinicia, commit falha, broker reentrega, deploy interrompe processo. O consumidor precisa produzir o mesmo resultado quando recebe a mesma mensagem novamente.

Algumas práticas:

1. inclua `event_id` único em toda mensagem importante;
2. grave eventos processados em uma tabela com chave única;
3. use operações idempotentes, como `upsert`;
4. não cobre duas vezes se o mesmo `pagamento_id` reaparecer;
5. se enviar email, registre envio por template e destinatário antes de repetir;
6. se chamar API externa, use idempotency key quando disponível.

Exemplo simples com banco:

```python
def processar_pagamento(evento):
    if evento_ja_processado(evento["event_id"]):
        return

    confirmar_pagamento(evento["pedido_id"], evento["valor"])
    marcar_evento_processado(evento["event_id"])
```

Em projeto real, essa marcação deve estar na mesma transação das mudanças importantes sempre que possível. Caso contrário, você troca duplicidade por outro tipo de inconsistência.

## Retry, dead letter queue e backoff

Falhas transitórias são normais: timeout em API externa, banco reiniciando, limite de rate, DNS instável. Reprocessar imediatamente em loop pode piorar tudo. O ideal é usar retry com backoff e limite.

Uma política comum:

1. primeira falha: retry rápido;
2. falhas seguintes: esperar 30 segundos, 2 minutos, 10 minutos;
3. depois do limite: enviar para dead letter queue;
4. registrar erro com contexto suficiente;
5. criar alerta quando a fila morta cresce.

Dead letter queue não é lixeira. É uma fila de investigação. Ela guarda mensagens que não puderam ser processadas automaticamente. O time precisa de painel, logs e procedimento para corrigir dados, reprocessar ou descartar com justificativa.

## Contrato de mensagem

Mensagem sem contrato vira acoplamento invisível. Se um produtor muda `valor` de número para string, um consumidor pode quebrar horas depois. Para evitar isso, defina um formato explícito.

Um evento simples:

```json
{
  "event_id": "evt_01HX...",
  "event_type": "pedido_pago",
  "occurred_at": "2026-06-04T11:00:00Z",
  "schema_version": 1,
  "payload": {
    "pedido_id": "ped_123",
    "cliente_id": "cli_456",
    "valor": 199.9,
    "moeda": "BRL"
  }
}
```

Com Python, você pode validar esse contrato com [Pydantic](/blog/pydantic-validacao-dados-python/):

```python
from datetime import datetime

from pydantic import BaseModel


class PedidoPagoPayload(BaseModel):
    pedido_id: str
    cliente_id: str
    valor: float
    moeda: str = "BRL"


class EventoPedidoPago(BaseModel):
    event_id: str
    event_type: str
    occurred_at: datetime
    schema_version: int
    payload: PedidoPagoPayload
```

Validar mensagem na borda do consumidor economiza horas de debug. Se o contrato muda, incremente `schema_version` e mantenha compatibilidade enquanto consumidores antigos ainda existem.

## Arquitetura para portfólio Python

Um projeto de portfólio convincente não precisa simular uma big tech. Ele precisa demonstrar decisões profissionais. Uma boa ideia:

```text
api-vagas-python/
  app/
    main.py
    settings.py
    events.py
  worker/
    enviar_alertas.py
    atualizar_metricas.py
  tests/
  compose.yaml
  pyproject.toml
  README.md
```

Fluxo:

1. a API recebe uma nova vaga Python;
2. salva no PostgreSQL;
3. publica `vaga_publicada` em RabbitMQ;
4. um worker envia alerta para assinantes interessados;
5. outro worker atualiza métricas por tecnologia;
6. logs registram `event_id`, `job_id` e tempo de processamento.

Esse projeto conversa diretamente com vagas que pedem Python, FastAPI, PostgreSQL, Docker, RabbitMQ, Kafka, Redis, observabilidade e testes. Para fortalecer, inclua testes de unidade para contrato de mensagem, um teste de integração simples e um README com diagrama do fluxo.

## Cuidados de segurança e LGPD

Mensagens costumam carregar dados sensíveis sem que o time perceba. Não publique CPF, token, senha, endereço completo, dados bancários ou conteúdo pessoal em tópico compartilhado sem necessidade clara. Quanto mais consumidores têm acesso a um tópico, maior a superfície de vazamento.

Boas práticas:

1. envie identificadores, não dados completos, quando possível;
2. masque dados sensíveis nos logs;
3. use TLS e autenticação no broker;
4. separe ambientes de dev, staging e produção;
5. controle permissões por tópico ou fila;
6. defina retenção adequada;
7. documente quem consome cada evento.

Essa disciplina vale para projetos pequenos também. Portfólio com dado fictício e cuidado explícito passa muito mais confiança do que demo que imprime segredo no terminal.

## Observabilidade para filas e eventos

Quando uma requisição síncrona falha, o usuário normalmente vê erro. Quando uma mensagem falha, o problema pode ficar escondido. Por isso mensageria exige observabilidade.

Monitore pelo menos:

1. tamanho da fila ou lag do consumer group;
2. taxa de mensagens publicadas e consumidas;
3. tempo médio de processamento;
4. quantidade de retries;
5. mensagens na dead letter queue;
6. erros por tipo de evento;
7. idade da mensagem mais antiga pendente.

Nos logs, inclua `event_id`, `event_type`, `consumer`, `attempt`, `status` e duração. Se usar OpenTelemetry, propague trace context quando possível. Isso conecta a requisição original ao processamento assíncrono e facilita entender onde o fluxo travou.

## Erros comuns

Alguns erros aparecem repetidamente em projetos Python com mensageria:

1. confirmar mensagem antes de terminar o trabalho;
2. não ter idempotência;
3. usar Kafka para tarefa simples de email;
4. colocar payload gigante na mensagem em vez de referência para storage;
5. não versionar contrato;
6. logar dados sensíveis;
7. não ter dead letter queue;
8. ignorar lag e descobrir atraso só por reclamação de usuário;
9. misturar evento de domínio com comando operacional;
10. não documentar ordem esperada dos eventos.

Evitar esses pontos já coloca seu projeto acima da maioria das demos. O objetivo não é construir arquitetura perfeita, mas deixar claro quais falhas você antecipou.

## Como escolher no seu projeto

Use esta regra prática:

| Situação | Escolha provável |
|---|---|
| enviar email simples depois da resposta HTTP | BackgroundTasks ou Celery |
| processar arquivos, PDFs ou planilhas com status | Celery, RabbitMQ ou tabela de jobs |
| vários serviços reagem ao mesmo evento de negócio | Kafka ou RabbitMQ com fanout |
| analytics precisa reler eventos antigos | Kafka |
| produto pequeno precisa de auditoria simples | tabela no PostgreSQL |
| cache, rate limit e fila leve no mesmo stack | Redis ou Redis Streams |

Se estiver estudando para backend Python, comece com RabbitMQ ou Celery porque o ciclo mental fica mais direto: produzir tarefa, consumir, confirmar, retry, dead letter. Depois avance para Kafka quando estudar eventos, streaming, data engineering e analytics.

## Próximos passos

Mensageria é uma habilidade prática, não apenas tema de arquitetura. Para aprender de verdade, crie um projeto pequeno com Docker Compose, uma API FastAPI, PostgreSQL, RabbitMQ ou Kafka, dois consumidores e logs estruturados. Escreva no README como lidar com duplicidade, retry e falha de consumidor. Isso demonstra maturidade para vagas de backend, dados, DevOps e automação.

Para continuar, aprofunde em [FastAPI Background Tasks, Celery e Redis](/blog/fastapi-background-tasks-celery-redis-2026/), [Python e Redis](/blog/python-e-redis/), [OpenTelemetry em Python](/blog/opentelemetry-python-observabilidade/), [testes com pytest](/guias/testes-com-pytest/) e [deploy de aplicações Python](/blog/deploy-aplicacao-python/). Se quiser comparar com outra linguagem usada em alta concorrência, veja também o conteúdo sobre <a href="https://golang.com.br/blog/worker-pool-go-fila-jobs/" target="_blank" rel="noopener noreferrer" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br/blog/worker-pool-go-fila-jobs' })">worker pools e filas em Go</a>.
