01 - Apache Kafka (Alta performance, streaming)
video: https://youtu.be/... no frontmatter da nota.Apache Kafka (Alta performance, streaming)
Apache Kafka é uma plataforma distribuída de streaming de eventos, organizada em torno de um log appendido durável e particionado. Em vez de entregar e descartar mensagens, o Kafka as retém por um período configurado ou indefinidamente, permitindo que múltiplos consumidores leiam o mesmo fluxo em momentos diferentes e que dados sejam reprocessados do início. Essa combinação de throughput elevado, ordenação por partição e replay histórico tornou o Kafka a espinha dorsal de pipelines de dados, integração entre domínios, CDC de bancos de dados e processamento de eventos em tempo real em escala de produção. Sua relevância para arquitetura de produção é o papel de barramento central: quando o fluxo de eventos de um negócio passa pelo Kafka, ele define ordenação, retenção e capacidade de reprocessamento de toda a plataforma. Desde o Kafka 4.0, lançado em 2025, a plataforma roda exclusivamente no modo KRaft, com consenso Raft embutido para metadados, encerrando definitivamente a dependência do ZooKeeper.
O modelo de log appendido e partições
O fundamento do Kafka é o log: uma sequência imutável de registros em que cada evento é acrescido ao final e recebe um offset sequencial. Nada é editado ou removido no meio; a remoção acontece por política de retenção ou por compactação de chaves. Esse desenho permite escrita sequencial em disco, que é o motivo do throughput brutal, e leitura eficiente a partir de qualquer ponto por offset.
O tópico é dividido em partições, e cada partição é um log independente com seu próprio offset. A partição é a unidade de paralelismo e de ordenação: mensagens com a mesma chave vão sempre para a mesma partição, garantindo ordem total dentro dela e nenhuma garantia entre partições. Um tópico de pedidos particionado por pedido_id garante a ordem de eventos de um pedido; a ordem global entre pedidos não existe, e quem precisa dela deve desenhar a chave com cuidado.
A replicação funciona por partição: cada partição tem um líder e réplicas em brokers diferentes, e a escrita é confirmada de acordo com o fator acks configurado pelo produtor. acks=1 confirma no líder, mais rápido e menos durável; acks=all confirma no conjunto em sincronia, mais lento e resistente à falha de um broker. Em produção com dados que não podem ser perdidos, acks=all com mínimo de réplicas em sincronia igual a 2 é o ponto de partida.
# Configuração de produtor focada em durabilidade (props ilustrativas)
bootstrap.servers=broker1:9092,broker2:9092,broker3:9092
acks=all # espera confirmação do conjunto em sincronia
enable.idempotence=true # evita duplicatas por retry do próprio produtor
max.in.flight.requests.per.connection=5
compression.type=lz4 # lote comprimido, economiza banda e disco
transactional.id=pedidos-produtor # habilita exatamente-uma-vez por transação
Arquitetura do cluster sob KRaft
Um cluster KRaft tem dois papéis distribuídos entre os brokers. Os controladores formam o quórum Raft que gerencia os metadados, como catálogo de tópicos, listas de réplicas e configurações, e os brokers servem dados a produtores e consumidores. Um mesmo nó pode acumular os dois papéis em clusters menores, e ambientes grandes os separam para isolar falhas.
A consequência operacional é que o Kafka passou a ser um único sistema distribuído. Antes, um incidente podia exigir diagnóstico simultâneo do ZooKeeper e do Kafka, com duas curvas de aprendizado, dois planos de backup e dois modos de falha; agora há um plano só. A recuperação de controlador usa o próprio consenso Raft, e a eleição de líder de partição segue sendo responsabilidade do controlador ativo.
O dimensionamento continua sendo por partições e réplicas, e clusters com dezenas de milhares de partições se comportam melhor sob KRaft porque o snapshot de metadados substitui a sincronização completa por watch que o ZooKeeper fazia. Isso importa em produção porque o crescimento orgânico de tópicos tende a explodir a contagem de partições antes que o time perceba.
Consumer groups e rebalanceamento
Consumidores se organizam em grupos, e o Kafka distribui as partições entre os membros do grupo: cada partição é atribuída a exatamente um consumidor do grupo por vez. Isso entrega o padrão de concorrência sem lock externo; escalar consumo é adicionar membros ao grupo, até o limite do número de partições, a partir do qual novos membros ficam ociosos.
Cada grupo mantém offsets independentes, o que permite que faturamento, analytics e busca leiam o mesmo tópico em ritmos diferentes sem se afetar. Essa é a diferença estrutural para filas tradicionais: no Kafka, a mensagem não é removida ao ser consumida, e a posição de leitura é responsabilidade de cada grupo.
O commit de offset é a chave da semântica de entrega. Confirmar offset depois de processar produz at-most-once em caso de queda entre processamento e commit; confirmar antes de processar produz at-least-once, com reprocesso na queda, e é o padrão na maioria dos fluxos, combinado com idempotência do consumidor. Transações do produtor e leitura isolada de transações fecham a oferta até exatamente-once no escopo Kafka a Kafka, com custo de latência e complexidade que precisa se justificar.
O rebalanceamento é o processo de redistribuição de partições quando membros entram, saem ou morrem. Ele é necessário, mas é também o evento mais disruptivo do dia a dia: durante um rebalanceamento com o protocolo clássico, o consumo para, e rebalanceamentos frequentes causados por liveness instável derrubam a taxa de processamento. Controles essenciais: heartbeat e session timeout calibrados, max.poll.interval maior que o processamento mais lento, e protocolo cooperativo, que move apenas as partições necessárias e mantém o restante consumindo.
Retenção e replay
A retenção é configurável por tempo, como 7 dias, por tamanho, ou ilimitada; com compactação por chave, o log mantém o último valor de cada chave, útil para estados derivados. A consequência arquitetural é que o Kafka é ao mesmo tempo canal de transporte e repositório temporário de eventos, o que habilita o replay.
O replay é a capacidade de reposicionar o offset de um grupo e reconsumir o fluxo. Em produção, é a ferramenta que resolve classes inteiras de incidentes: um bug no consumidor de projeções corrompeu o índice de busca, a correção entra, o offset volta para o início da janela retida, e o índice é reconstruído a partir dos eventos; nenhum dado novo precisa ser recriado manualmente. Para isso funcionar, o consumidor precisa ser idempotente, porque o replay reentrega eventos já processados.
Retenção cobra custo de armazenamento e de operação, e a janela de replay deve ser decidida junto com o negócio: quantos dias de reconstrução fazem sentido. Tópicos de eventos de negócio costumam pedir retenção longa; tópicos intermediários de pipeline pedem curta.
A compactação por chave é a variante para estado: o tópico mantém o último valor de cada chave, e um consumidor novo consegue reconstruir a visão atual lendo o log compactado do início. É o mecanismo por trás de tópicos de configuração e de snapshots de estado em Kafka Streams.
Kafka 4.0 e o fim do ZooKeeper
O modo KRaft substitui o ZooKeeper por um protocolo de consenso Raft embutido no próprio cluster Kafka. Um conjunto de controladores mantém os metadados, como tópicos, partições e configurações, e replica essas informações por consenso, eliminando o segundo sistema distribuído que precisava ser operado, monitorado e dimensionado junto.
O Kafka 4.0, de 2025, removeu o ZooKeeper definitivamente, e o modo KRaft passou a ser o único suportado. Os ganhos práticos de operação são concretos: uma tecnologia a menos no stack, escalonamento de metadados mais previsível em clusters com muitas partições, failover de controlador mais rápido e um modelo de configuração unificado. Serviços gerenciados acompanham a transição, e o Amazon MSK já oferece upgrade in-place de clusters ZooKeeper para KRaft, reduzindo a barreira de migração para quem opera em nuvem.
Para quem ainda opera clusters legados, a migração é projeto de arquitetura: versões intermediárias suportam modo duplo, em que o cluster migra metadados progressivamente, e o plano precisa cobrir dependências que ainda conversam com o ZooKeeper diretamente, prática que nunca deveria existir e ainda existe.
# Operação essencial do Kafka via CLI (KRaft)
kafka-topics.sh --bootstrap-server broker1:9092 \
--create --topic pedidos --partitions 12 --replication-factor 3 \
--config retention.ms=604800000 \
--config min.insync.replicas=2
# Produzir com chave: mesma chave sempre cai na mesma partição
kafka-console-producer.sh --bootstrap-server broker1:9092 \
--topic pedidos --property parse.key=true --property key.separator=:
# Consumir como grupo, do primeiro offset retido
kafka-console-consumer.sh --bootstrap-server broker1:9092 \
--topic pedidos --group faturamento --from-beginning
# Descrever grupo: lag por partição é a métrica de saúde do consumo
kafka-consumer-groups.sh --bootstrap-server broker1:9092 \
--describe --group faturamento
# Reprocessar: reposicionar o grupo para o início do log retido
kafka-consumer-groups.sh --bootstrap-server broker1:9092 \
--group faturamento --topic pedidos --reset-offsets --to-earliest --execute
Configurações que decidem produção
| Configuração | Valor conservador | Efeito em produção |
|---|---|---|
| acks | all | Sem perda com réplica em sincronia, latência maior |
| min.insync.replicas | 2 | Rejeita escrita se sincronia cair abaixo, protege durabilidade |
| enable.idempotence | true | Elimina duplicação por retry do produtor |
| retention.ms | Por janela de replay decidida com o negócio | Define até onde o reprocesso alcança |
| num.partitions | Taxa esperada dividida por capacidade de consumo | Limite físico do paralelismo do grupo |
| max.poll.interval.ms | Maior que o lote mais lento | Evita expulsão do consumidor e rebalanceamento em loop |
| cleanup.policy | delete para fluxo, compact para estado | Compact mantém último valor por chave |
Cenários reais de produção
CDC de banco de dados para busca: o time precisa que o índice de busca reflita alterações de produto em segundos. Um conector CDC publica cada mudança do binlog em um tópico por tabela, o consumidor atualiza o índice, e a ordem por chave de produto garante que a última versão vence. Quando o índice corrompe por bug, o replay da janela retida reconstrói tudo sem tocar o banco de origem.
Integração entre domínios de um e-commerce: pedidos, estoque e faturamento conversam por eventos. O tópico de pedidos tem 24 partições por chave de pedido, três times consomem o mesmo fluxo com grupos distintos, e a inclusão do quarto consumidor, antifraude, foi apenas criar um grupo novo. Nenhuma mudança de código no serviço de pedidos.
Pico de ingestão em evento ao vivo: uma transmissão gera 200 mil eventos por segundo de reações de audiência. Produtores com batching e compressão, tópico com 48 partições e consumidores escalados processam o fluxo com lag controlado; a retenção de 24 horas permite recalcular métricas agregadas após correção de uma janela de processamento defeituosa.
Quando escolher Kafka
Kafka é a escolha natural quando o requisito é fluxo de eventos em alto volume com múltiplos consumidores independentes. Casos típicos: coleta de eventos de aplicação para analytics, CDC de banco para propagar mudanças, integração de microserviços orientada a eventos com replay, ingestão de pipelines de dados e streaming com processamento incremental.
Ele é escolha errada quando o problema é fila de tarefas simples. Para executar jobs em segundo plano com entrega um para um, ordenação flexível, roteamento complexo por padrão de assunto ou latência de milissegundos por mensagem individual, uma fila tradicional como RabbitMQ ou SQS é mais simples de operar e mais barata. Kafka exige disciplina de partições, chaves e offsets, e essa disciplina só se paga quando o volume e a necessidade de replay existem.
Um sinal de decisão prática: se múltiplos sistemas precisam ler os mesmos dados, se eventos precisam ser reprocessados ou se o volume passa de dezenas de milhares de mensagens por segundo, Kafka se paga. Se a demanda é distribuir tarefas entre workers, qualquer fila resolve com menos complexidade.
O caminho de adoção mais seguro em produção é começar por um domínio que já tenha os dois requisitos, volume e replay, operar o tópico com retenção generosa no início, e expandir para os demais domínios conforme a operação amadurece. O custo reverso também existe: extrair Kafka de uma plataforma que o adotou por modismo é difícil, porque consumidores crescem sobre o log rápido.
Erros comuns e boas práticas
- Usar número de partições pequeno demais: é o teto do paralelismo e exige migração dolorosa depois; dimensionar para o pico futuro com folga, porque mais partições também custam recursos e rebalanceamento.
- Chave de partição mal escolhida: chaves de baixa cardinalidade criam partições quentes, e uma partição quente limita o throughput de todo o tópico.
- Processar lento demais entre polls: max.poll.interval estoura, o membro é expulso, o rebalanceamento recomeça, e o grupo entra em loop de instabilidade; lote deve caber na janela.
- Tratar ordem entre partições como garantida: ordem existe só dentro da partição; desenhar a chave como a entidade cuja ordem importa.
- Reprocessar sem idempotência: replay reentrega eventos; sem idempotência, o reprocesso duplica efeitos em vez de consertar dados.
- Ignorar lag do grupo: lag é o sintoma central de saúde; alarme deve existir para lag absoluto e para tendência de crescimento, porque lag estável alto também significa dado atrasado.
- Manter dependência de ZooKeeper em ferramentas internas: com Kafka 4.0 e KRaft único, toda integração direta com ZooKeeper vira dívida de migração; usar APIs do Kafka ou do schema registry.
- Subestimar operação de disco e rede: Kafka é intensivo em I/O sequencial; provisionar SSD para logs e monitorar throughput de rede entre brokers e produtores antes do primeiro incidente de saturação.
- Comitar offset com frequência exagerada: commit por mensagem multiplica latência e carga de metadados; comitar por lote ou por janela curta equilibra reprocesso máximo e performance.
- Migrar para KRaft sem inventariar dependências: ferramentas de monitoramento, scripts de operação e SDKs antigos que tocam ZooKeeper diretamente quebram silenciosamente; inventário antes do upgrade in-place.
- Deixar o schema das mensagens sem registro central: produtor e consumidor de times diferentes quebram contrato em deploy independente; schema registry com regra de compatibilidade evita incidente de serialização.