Guias / kafka
Kafka
Plataforma distribuída de streaming de eventos: o log, tópicos e partições, produtores e consumidores, replicação, garantias de entrega, filas com share groups, ecossistema e padrões de produção.
31 jan 2025· 10 min· 15 seções
Neste guia · 15 seções
Por que Kafka
Quando cada sistema conversa direto com os outros, cada nova integração é mais um fio, e o número de fios cresce rápido. O Kafka coloca um log de eventos central no meio: quem produz escreve uma vez, e quem precisa consome quando e quantas vezes quiser.
“Plataforma distribuída de streaming de eventos de alta performance”:
- Alta vazão e baixa latência: milhões de mensagens por segundo num cluster.
- Escalável horizontalmente: mais brokers, mais partições.
- Armazenamento: as mensagens não somem ao serem lidas; ficam pelo tempo configurado.
- Alta disponibilidade, por replicação entre máquinas.
- Conectividade: conectores e bibliotecas prontas para quase tudo.
O log: a ideia central
Cada partição de um tópico é um log: uma sequência de mensagens que só cresce no final. Cada mensagem recebe um número sequencial, o offset. Consumidores não apagam nada; cada um só guarda até onde já leu.
Por isso dois consumidores podem estar em pontos diferentes do mesmo log, um consumidor novo pode ler tudo desde o começo e, depois de corrigir um bug, dá para voltar o offset e reprocessar.
Laboratório: Kafka local em dois minutos
Com Docker, um broker de verdade (já em modo KRaft) sobe com um comando:
docker run -d --name kafka -p 9092:9092 apache/kafka:4.2.2Os scripts de linha de comando ficam em /opt/kafka/bin dentro do container:
# cria um tópico com 3 partiçõesdocker exec -it kafka /opt/kafka/bin/kafka-topics.sh --bootstrap-server localhost:9092 \ --create --topic pedidos --partitions 3
# terminal 1: produz mensagens no formato chave:valordocker exec -it kafka /opt/kafka/bin/kafka-console-producer.sh --bootstrap-server localhost:9092 \ --topic pedidos --property parse.key=true --property key.separator=:
# terminal 2: consome desde o início, mostrando chave e partiçãodocker exec -it kafka /opt/kafka/bin/kafka-console-consumer.sh --bootstrap-server localhost:9092 \ --topic pedidos --from-beginning --property print.key=true --property print.partition=trueEnvie pedido-42:criado, pedido-7:criado e pedido-42:pago: os dois eventos do pedido 42 sempre caem na mesma partição.
Arquitetura do cluster
- Broker: um servidor Kafka. Guarda em disco as partições pelas quais é responsável e atende produtores e consumidores.
- Cluster: um conjunto de brokers. Em produção, no mínimo 3, para tolerar a queda de um.
- KRaft: o protocolo de consenso com que os próprios brokers (ou nós dedicados, os controllers) mantêm os metadados do cluster: quais tópicos existem, quem lidera cada partição, quais brokers estão vivos. Substituiu o ZooKeeper, que foi removido no Kafka 4.0.
Tópicos, partições e chaves
Um tópico é um canal com nome (pedidos, pagamentos). Ele é dividido em partições, espalhadas pelos brokers, que distribuem a carga e aumentam a vazão. A ordem só é garantida dentro de cada partição.
Cada mensagem tem:
- Key: decide a partição (por hash). Mesma chave, mesma partição, ordem garantida entre elas. Sem chave, as mensagens são distribuídas entre as partições.
- Value: o conteúdo (JSON, Avro, Protobuf).
- Headers: metadados (trace id, versão do schema).
- Timestamp.
Quantas partições?
A quantidade de partições é o teto de paralelismo de um consumer group. Dá para aumentar depois, mas não diminuir, e aumentar muda para qual partição cada chave vai, quebrando a ordem durante a transição. Comece com folga (ex.: 6 ou 12 para um tópico com tráfego relevante).
Retenção e compactação
- Por tempo ou tamanho (
retention.ms, padrão de 7 dias): mensagens antigas são apagadas, tenham sido lidas ou não. - Compactação (
cleanup.policy=compact): mantém só a última mensagem de cada chave. Transforma o tópico numa tabela com o estado atual (ex.: o endereço mais recente de cada cliente).
Produtores
O produtor escolhe quanta confirmação espera com acks:
acks | Confirmação | Risco |
|---|---|---|
0 | Nenhuma: envia e segue | Pode perder mensagens sem saber |
1 | O líder gravou | Perde se o líder cair antes de replicar |
all (ou -1) | Todas as réplicas em dia (ISR) gravaram | O mais seguro; padrão desde o Kafka 3.0 |
Desde o 3.0, o produtor também é idempotente por padrão: retries de rede não geram mensagens duplicadas nem fora de ordem na partição. Para vazão, ele agrupa mensagens em lotes (linger.ms, batch.size) e pode comprimi-las (compression.type=zstd).
var props = new Properties();props.put("bootstrap.servers", "localhost:9092");props.put("acks", "all");props.put("key.serializer", StringSerializer.class.getName());props.put("value.serializer", StringSerializer.class.getName());
try (var producer = new KafkaProducer<String, String>(props)) { // a mesma chave garante a ordem dos eventos deste pedido producer.send(new ProducerRecord<>("pedidos", "pedido-42", "{\"status\":\"PAGO\"}"), (meta, error) -> { if (error != null) log.error("falha ao publicar", error); });}Consumidores
Consumer groups
Consumidores com o mesmo group.id dividem as partições entre si: cada partição é lida por um consumidor do grupo. Grupos diferentes leem tudo, de forma independente.
Consequência: mais consumidores do que partições num grupo não aumenta a vazão; os excedentes ficam parados, de reserva.
Offsets e commit
O consumidor lê em laço (poll) e confirma (commit) até onde processou. O offset confirmado fica guardado no próprio Kafka, e é de onde o grupo continua depois de um restart.
props.put("group.id", "faturamento");props.put("enable.auto.commit", "false"); // confirma só depois de processar
try (var consumer = new KafkaConsumer<String, String>(props)) { consumer.subscribe(List.of("pedidos")); while (true) { var records = consumer.poll(Duration.ofMillis(500)); for (var record : records) { process(record.key(), record.value()); } consumer.commitSync(); }}Com Spring Boot, o laço, o commit e as threads ficam com o framework:
@KafkaListener(topics = "pedidos", groupId = "faturamento")void onOrder(ConsumerRecord<String, String> record) { process(record.key(), record.value());}Rebalance
Quando um consumidor entra, sai ou trava, o grupo redistribui as partições. Desde o Kafka 4.0 está pronto para produção um novo protocolo de rebalance (KIP-848), coordenado pelo broker, que move só as partições necessárias em vez de parar o grupo inteiro. Ele já vem ativo nos brokers, mas no cliente o padrão ainda é o protocolo clássico: para usar o novo, configure group.protocol=consumer.
Lag
Lag é a distância entre a última mensagem do tópico e o offset confirmado pelo grupo: quanto trabalho está acumulado. É a métrica mais importante de um consumidor.
docker exec -it kafka /opt/kafka/bin/kafka-consumer-groups.sh --bootstrap-server localhost:9092 \ --describe --group faturamentoReplicação
- Fator de replicação: quantas cópias de cada partição existem no cluster, contando a principal. O comum é 3.
- Líder: cada partição tem um líder, que recebe as escritas. As outras cópias (followers) replicam a partir dele.
- ISR (in-sync replicas): as réplicas em dia com o líder. Se o líder cair, uma réplica do ISR assume, e o tópico continua disponível.
min.insync.replicas=2comacks=all: a escrita só é confirmada se pelo menos duas cópias a tiverem. Com fator 3, o cluster tolera perder um broker sem perder dados nem parar.
Garantias de entrega
| Semântica | Como acontece | Efeito |
|---|---|---|
| No máximo uma vez | Confirma o offset antes de processar | Uma falha no meio perde a mensagem |
| Pelo menos uma vez | Confirma depois de processar (o padrão recomendado) | Uma falha no meio reprocessa a mensagem |
| Exatamente uma vez | Transações do Kafka: ler, processar e escrever em outro tópico de forma atômica | Só vale dentro do Kafka; efeitos externos (e-mail, outro banco) ainda podem repetir |
Na prática, use pelo menos uma vez e torne o consumidor idempotente: processar a mesma mensagem duas vezes não pode causar efeito duplicado (ex.: guardar o id do evento já processado). Veja o glossário.
Filas no Kafka: share groups
Tópicos e filas clássicas resolvem problemas diferentes:
| Tópico com consumer group | Fila (share group ou RabbitMQ) | |
|---|---|---|
| Distribuição | Uma partição inteira por consumidor | Mensagem a mensagem, para qualquer consumidor livre |
| Paralelismo | Limitado ao número de partições | Independente das partições |
| Ordem | Garantida por partição | Não garantida |
| Confirmação | Por offset (tudo até ali) | Individual, por mensagem |
| Bom para | Eventos, streaming, reprocessamento, ordem por chave | Distribuir tarefas entre workers |
Desde o Kafka 4.2 (fevereiro de 2026), os share groups trazem a semântica de fila para dentro do Kafka. Cada mensagem é entregue a um consumidor com uma trava temporária, e ele responde:
- ACCEPT: processou, não entrega de novo.
- RELEASE: falha temporária, devolve para outro consumidor tentar.
- REJECT: não dá para processar, descarta.
Se a trava expirar sem resposta, a mensagem volta para a fila, e depois de algumas tentativas (5, por padrão) é descartada, o que evita que uma mensagem com defeito fique em loop. No Java, o cliente é o KafkaShareConsumer.
Ecossistema
- Kafka Connect: move dados entre o Kafka e outros sistemas sem código, com conectores. Combinado com o Debezium, faz CDC (change data capture): cada alteração em uma tabela do banco vira um evento.
- Kafka Streams: biblioteca Java para processar streams (filtrar, agregar, juntar tópicos) dentro da sua aplicação.
- Schema Registry (Confluent, fora do projeto Apache): guarda os schemas (Avro, Protobuf, JSON Schema) e impede que um produtor publique algo que quebre os consumidores.
- Apache Flink: processamento de streams em larga escala, com estado e janelas de tempo.
Padrões em produção
- Outbox: para publicar um evento sem risco de inconsistência com o banco, grave-o numa tabela na mesma transação e deixe outro processo publicá-lo. Veja a nota Transactional Outbox.
- Retry e dead letter topic: uma mensagem que falha não pode travar a partição. Envie para um tópico de retry com atraso e, depois de N tentativas, para um dead letter topic, com alerta.
- Consumidores idempotentes: assuma que toda mensagem pode chegar duas vezes.
- Monitore o lag de cada consumer group, não só a saúde dos brokers.
- Contratos versionados: evolua os schemas de forma compatível; nunca mude o significado de um campo existente.
Quando usar
| Use Kafka | Prefira outra coisa |
|---|---|
| Eventos que vários sistemas precisam consumir | Poucas mensagens e um único consumidor (uma fila simples basta) |
| Reprocessar o histórico (novo consumidor, correção de bug) | Requisição e resposta síncronas (use HTTP ou gRPC) |
| Alto volume: logs, métricas, cliques, CDC de bancos | Time pequeno sem quem opere um cluster (considere um serviço gerenciado) |
Veja também: System design, CQRS e Observabilidade.
Vídeos
- What is Apache Kafka? (Tim Berglund, Confluent): os conceitos com ótimas analogias (é o vídeo embutido no topo). É de 2019, então ainda fala do ZooKeeper, que hoje foi substituído pelo KRaft.
- Apache Kafka in 6 minutes (James Cutajar): partições, offsets e replicação com um exemplo, bem direto.
Referências
- Introdução ao Apache Kafka: eventos, tópicos, partições e os principais conceitos (documentação oficial).
- Quickstart: o passo a passo oficial, com Docker e com os binários.
- Anúncio do Kafka 4.2: share groups prontos para produção.
- Kafka share groups explained: confirmações, travas e limites de entrega (Conduktor).