Dominando a Configuração de Tópicos Kafka: Um Guia Abrangente

Um guia prático sobre partições, replicação, retenção, compactação e alterações seguras de configuração em tópicos Kafka.

Dominando a Configuração de Tópicos Kafka: Um Guia Abrangente

A configuração de tópicos Kafka decide como seus dados são armazenados, copiados, expirados, compactados e consumidos. Você pode executar Kafka com as configurações padrão por um tempo, especialmente em um cluster de desenvolvimento, mas tópicos de produção precisam de mais cuidado. Uma contagem ruim de partições pode prender uma carga de trabalho pesada. Replicação fraca pode transformar uma falha de broker em perda de dados. Retenção frouxa pode encher discos. A compactação pode surpreender se as chaves estiverem ausentes ou inconsistentes.

A maneira útil de abordar a configuração de tópicos Kafka não é memorizar todas as configurações. Comece com as perguntas que um sistema real faz: quanta paralelismo eu preciso, por quanto tempo os dados devem permanecer disponíveis, quantos dados posso armazenar, o que acontece durante uma falha de broker e os consumidores precisam de um histórico completo de eventos ou apenas do valor mais recente por chave?

Um tópico é dividido em partições. Cada partição é um log ordenado. O Kafka preserva a ordem dentro de uma partição, não em todo o tópico. Se todos os eventos para um cliente devem ser processados em ordem, use uma chave estável como customer_id para que esses eventos caiam na mesma partição. Se você chavear aleatoriamente, pode obter melhor distribuição, mas perder a ordenação por entidade.

A contagem de partições é uma das primeiras escolhas que as pessoas se arrependem. Mais partições permitem mais paralelismo de consumidores porque, dentro de um grupo de consumidores, uma partição é consumida por apenas um membro do grupo por vez. Se um tópico tem seis partições, um grupo de consumidores pode usar ativamente até seis consumidores para esse tópico. Adicionar um sétimo consumidor não aumentará o consumo para esse tópico, a menos que haja outras partições atribuídas.

Mais partições também custam algo. Elas aumentam metadados, arquivos abertos, trabalho de replicação, trabalho de eleição de líder e tempo de recuperação após falhas de broker. Contagens muito altas de partições podem tornar as operações do cluster mais lentas, mesmo que cada partição tenha tráfego modesto. Não há um número universal melhor. Um pequeno tópico interno pode ser bom com três partições. Um fluxo de eventos movimentado pode precisar de dezenas. Uma instalação Kafka muito grande pode usar muito mais, mas isso deve vir de throughput medido e capacidade operacional, não de hábito.

Crie um tópico com configurações explícitas:

kafka-topics.sh --create   --bootstrap-server broker1:9092   --topic user-events.v1   --partitions 12   --replication-factor 3   --config min.insync.replicas=2

O nome do tópico também deve carregar alguma intenção. Nomes como events ou data tornam-se inúteis assim que o cluster cresce. user-events.v1, billing-invoices.v1 ou inventory-adjustments.v1 informa aos futuros operadores o que é o fluxo e dá espaço para uma mudança de esquema disruptiva posteriormente.

O fator de replicação controla quantas cópias o Kafka mantém para cada partição. Em produção, 3 é um padrão comum porque permite que um broker falhe enquanto ainda deixa outra réplica disponível. Isso não significa que você pode ignorar as configurações do produtor. Se os produtores usarem acks=1, o Kafka pode reconhecer registros antes que os seguidores os copiem. Para tópicos importantes, combine o fator de replicação três com min.insync.replicas=2 no nível do tópico e acks=all do produtor.

min.insync.replicas é frequentemente mal compreendido. Ele não cria réplicas. Ele diz quantas réplicas em sincronia devem estar disponíveis para que uma gravação acks=all seja bem-sucedida. Com fator de replicação três e min.insync.replicas=2, o tópico pode tolerar um broker indisponível. Se apenas uma réplica em sincronia permanecer, o Kafka deve rejeitar gravações fortes em vez de aceitar dados com poucas cópias seguras.

As configurações de retenção decidem quando o Kafka pode excluir segmentos de log antigos. A retenção baseada em tempo é controlada por retention.ms no nível do tópico. A retenção baseada em tamanho é controlada por retention.bytes. Nomes mais antigos no nível do broker, como log.retention.ms, são padrões do broker; a configuração do tópico comumente usa retention.ms.

Por exemplo, para reter um tópico por sete dias:

kafka-configs.sh --alter   --bootstrap-server broker1:9092   --entity-type topics   --entity-name user-events.v1   --add-config retention.ms=604800000

Para limitar o armazenamento por partição, use retention.bytes:

kafka-configs.sh --alter   --bootstrap-server broker1:9092   --entity-type topics   --entity-name user-events.v1   --add-config retention.bytes=10737418240

Lembre-se de que retention.bytes é geralmente por partição, não o tamanho total do tópico. Um tópico com doze partições e retention.bytes=10GB pode usar aproximadamente 120GB antes da replicação e aproximadamente 360GB com fator de replicação três. Este é o tipo de detalhe que causa alertas de disco surpresa.

O Kafka exclui dados por segmento de log, não registro por registro. Se você definir um período de retenção curto, mas segmentos grandes, a exclusão pode não acontecer no minuto exato que você espera. Configurações de segmento como segment.bytes e segment.ms influenciam quando o Kafka rola para um novo segmento, e apenas segmentos fechados são elegíveis para exclusão ou compactação. Segmentos menores podem tornar a limpeza mais responsiva, mas adicionam sobrecarga.

cleanup.policy decide o que o Kafka faz com dados antigos. O padrão é delete, que remove segmentos antigos com base na retenção. compact mantém o registro mais recente para cada chave e eventualmente remove registros mais antigos com a mesma chave. Você também pode usar delete,compact para tópicos que precisam de compactação mais uma janela de retenção.

A compactação é útil para fluxos do tipo estado: atualizações de perfil de usuário, valores de feature flags, configurações de conta ou eventos de alteração de banco de dados chaveados por chave primária. É uma escolha ruim para histórico de eventos onde cada evento importa. Se você compactar um log de auditoria, eventos mais antigos para a mesma chave podem eventualmente desaparecer. Isso pode ser exatamente errado para conformidade ou depuração.

A compactação também depende de chaves. Um tópico compactado com chaves nulas ou inconsistentes não se comportará como um changelog chave-valor limpo. Se os produtores enviam atualizações de usuário às vezes chaveadas por user_id e às vezes chaveadas por email, o Kafka vê chaves diferentes. Ele não pode inferir que representam o mesmo usuário.

A compressão pode ser definida pelos produtores, e um tópico pode definir compression.type para controlar o comportamento do broker. Valores comuns incluem producer, gzip, snappy, lz4 e zstd, dependendo da versão do Kafka. Muitas equipes deixam o tópico como producer e padronizam a compressão do produtor. lz4 e zstd são escolhas comuns, mas a resposta certa depende do orçamento de CPU, formato da mensagem e pressão de rede.

Você pode inspecionar a configuração do tópico assim:

kafka-configs.sh --describe   --bootstrap-server broker1:9092   --entity-type topics   --entity-name user-events.v1

E inspecionar o posicionamento das partições assim:

kafka-topics.sh --describe   --bootstrap-server broker1:9092   --topic user-events.v1

Use ambos os comandos. As configurações do tópico informam retenção, compactação e regras ISR. A descrição do tópico informa líderes, réplicas e estado ISR. Um tópico pode ter configuração perfeita e ainda estar não saudável porque as réplicas estão fora de sincronia.

Algumas alterações são fáceis. Retenção, política de compactação, min.insync.replicas e várias outras configurações de tópico podem ser alteradas dinamicamente. Algumas alterações exigem mais cautela. Você pode aumentar a contagem de partições, mas não pode diminuí-la com segurança com um comando simples. Aumentar partições também altera a distribuição de chaves para registros futuros porque o cálculo de particionamento tem mais partições alvo. Registros existentes permanecem onde estão; novos registros para a mesma chave podem ir para uma partição diferente após o aumento, dependendo do particionador. Se a ordenação estrita por chave importa através da mudança, planeje com cuidado.

Alterações no fator de replicação são trabalho operacional. Aumentar réplicas para um tópico existente significa que o Kafka deve copiar dados existentes para novos brokers. Isso pode ser muita I/O. Use ferramentas de reassignment, monitore o progresso e limite se necessário. Não inicie um grande reassignment durante o pico de tráfego, a menos que você já saiba que o cluster tem capacidade sobressalente suficiente.

Para um tópico de evento de produção normal, um ponto de partida prático pode ser assim:

kafka-topics.sh --create   --bootstrap-server broker1:9092   --topic payments-authorized.v1   --partitions 24   --replication-factor 3   --config min.insync.replicas=2   --config retention.ms=1209600000   --config cleanup.policy=delete

Isso diz: partições suficientes para paralelismo, três cópias para disponibilidade, duas réplicas em sincronia necessárias para gravações fortes, quatorze dias de retenção e sem compactação porque cada evento de autorização de pagamento importa.

Para um tópico de estado, a forma é diferente:

kafka-topics.sh --create   --bootstrap-server broker1:9092   --topic user-preferences.v1   --partitions 12   --replication-factor 3   --config min.insync.replicas=2   --config cleanup.policy=compact

Esse tópico deve ser chaveado por ID de usuário. Consumidores reconstruindo estado podem ler o log compactado e eventualmente ver o valor mais recente para cada usuário. Eles não devem esperar que toda alteração histórica de preferência permaneça para sempre.

A melhor configuração de tópico é chata de operar. Tem partições suficientes, mas não milhares sem motivo. Tem replicação que corresponde ao valor dos dados. Tem retenção que corresponde às necessidades de recuperação e conformidade. Usa compactação apenas quando as chaves são significativas. É descrita em código ou documentação para que outro engenheiro possa recriá-la sem adivinhar.

Um hábito de revisão útil é escrever a história do consumidor antes de escolher as configurações do tópico. Quem lê este tópico? Eles precisam reproduzir desde o início? Quanto tempo levaria uma reconstrução completa? O sistema de origem pode republicar dados antigos? Se um consumidor ficar inativo por três dias, o Kafka ainda deve ter os registros perdidos? Essas respostas orientam a retenção de forma mais honesta do que uma configuração padrão de sete dias.

Considere um consumidor de detecção de fraude que lê eventos de pagamento. Se ficar inativo por seis horas, você quase certamente quer que ele se recupere do Kafka. Se ficar inativo por trinta dias, você pode esperar um processo de backfill separado do banco de dados de pagamentos. Esse tópico pode precisar de duas semanas de retenção, não para sempre. Um tópico de auditoria de segurança pode ter um requisito diferente, talvez enviando para armazenamento de objetos para retenção de longo prazo enquanto o Kafka mantém apenas a janela de reprodução ativa.

O tamanho da mensagem também pertence à conversa do tópico. O Kafka pode lidar com registros maiores quando configurado para isso, mas mensagens grandes afetam produtores, brokers, consumidores, replicação e memória de busca. Se as equipes começarem a colocar blobs JSON de vários megabytes ou arquivos codificados em um tópico, não apenas aumente max.message.bytes e siga em frente. Pergunte se a carga útil pertence ao armazenamento de objetos com uma referência no Kafka. O Kafka é geralmente melhor para mover eventos, não para atuar como um armazenamento de blobs.

A evolução do esquema não é uma configuração de tópico, mas molda o design do tópico. Um tópico nomeado com um sufixo de versão, como orders.v1, dá a você uma saída de emergência quando uma mudança disruptiva é inevitável. Mudanças compatíveis podem permanecer no mesmo tópico se consumidores e produtores seguirem uma política de esquema. Mudanças disruptivas não devem ser introduzidas no mesmo tópico porque uma equipe controla o produtor. O Kafka desacopla sistemas, mas apenas se o contrato for respeitado.

Finalmente, documente a propriedade do tópico. Cada tópico de produção deve ter uma equipe proprietária, produtores esperados, consumidores esperados, motivo de retenção e notas de sensibilidade de dados. Isso parece administrativo até que o disco encha às 02:00 e ninguém saiba se um tópico pode ser encurtado, excluído, compactado ou limitado. Uma boa configuração de tópico é parcialmente técnica e parcialmente memória operacional.

Uma verificação final antes de publicar um tópico é passar por um cenário de falha. Se um broker desaparecer, os produtores ainda podem escrever? Se um grupo de consumidores ficar inativo durante o fim de semana, a retenção cobrirá a lacuna? Se um produtor enviar dados ruins, os consumidores podem pular, colocar em quarentena ou reproduzir com segurança? Se o tópico crescer duas vezes mais rápido que o esperado, qual limite protege o cluster: tempo de retenção, bytes de retenção, cotas ou um alerta?

Cotas valem a pena mencionar porque a configuração do tópico sozinha não protege um cluster compartilhado de um produtor barulhento. O Kafka suporta cotas de cliente que podem limitar taxas de produção e busca. Se várias equipes compartilham um cluster, as cotas podem impedir que uma reprodução acidental ou produtor descontrolado sobrecarregue os brokers. Elas devem ser emparelhadas com alertas para que as equipes saibam que estão sendo limitadas em vez de culpar silenciosamente o Kafka.

Não se esqueça da política de exclusão. Alguns clusters desabilitam a exclusão de tópicos no nível do broker para evitar acidentes. Isso pode ser sensato, mas significa que tópicos abandonados devem ser tratados através de um processo de limpeza controlado. Uma revisão de inventário de tópicos a cada mês ou trimestre pode recuperar uma quantidade surpreendente de disco, especialmente em clusters de desenvolvimento e teste onde experimentos deixam tópicos antigos para trás.