Na New Relic, construímos algumas partes críticas de nosso pipeline com o Apache Kafka. Ao longo dos anos, encontramos muitos problemas, e desenvolvemos as melhores práticas para gerenciar nossos conjuntos Kafka. E aprendemos muito sobre como o Kafka funciona—tanto de forma eficaz como não tão eficaz—ao longo do caminho.O principal problema que encontramos envolve a configuração de confirmação automática do consumidor do Kafka—especificamente, como a perda ou duplicação de dados pode ocorrer quando o serviço do consumidor sofre uma interrupção por falta de memória (OOM), ou algum outro tipo de desligamento forçado.

NEW RELIC INTEGRAÇÃO KAFKA
kafka logo
Comece a monitoramento seus dados Kafka hoje mesmo.
Instale o início rápido do Kafka Instale o início rápido do Kafka

Me deixe explicar esse comportamento, e o que ele significa para os usuários do Kafka.(Observação: este blogpost pressupõe familiaridade com os fundamentos do Kafka. Para explorar mais sobre as integrações do Kafka com a New Relic One, veja Monitoramento do Kafka.)

O que é a confirmação automática do Kafka?

A confirmação automática do Kafka é uma opção de configuração que permite aos consumidores confirmar automaticamente as suas mensagens consumidas em intervalos regulares. Este intervalo pode ser configurado através da configuração auto.commit.interval.ms. Este modo é conveniente porque poupa os desenvolvedores de gerenciar manualmente o deslocamento. No entanto, tem a desvantagem de poder levar ao processamento duplicado de mensagens se um consumidor falhar e reiniciar, pois pode reprocessar mensagens que foram recebidas, mas ainda não foram confirmadas automaticamente. (Falaremos mais sobre isso em breve).

Em contraste, o modo de confirmação manual dá ao desenvolvedor mais controle sobre quando as compensações são confirmadas, permitindo um controle mais preciso da semântica de processamento de mensagens, como o processamento "pelo menos uma vez" ou "exatamente uma vez". Esse controle tem o custo de aumentar a complexidade do código da aplicação."

Como funciona a confirmação automática do consumidor do Kafka?

Quando a confirmação automática está habilitada, os consumidores do Kafka confirmam as suas compensações automaticamente, com base em um intervalo pré-definido. Esse intervalo determina a frequência com que o consumidor confirma o seu progresso para o corretor do Kafka. Este processo é controlado por duas propriedades de configuração principais:

  • Enable.auto.commit: esta propriedade é definida como verdadeira ou falsa. Quando definido como verdadeiro, as compensações do consumidor são confirmadas automaticamente em segundo plano, em intervalos regulares.
  • Auto.commit.interval.ms: esta propriedade especifica a frequência em milissegundos, na qual as compensações do consumidor são confirmadas no Kafka. Por exemplo, se for definido como 5.000, o consumidor confirma as compensações a cada 5 segundos.

Confirmação automática do consumidor do Kafka: diminuindo a perda e a duplicação de dados

Usar a confirmação automática do Kafka pode melhorar a eficiência e simplificar o desenvolvimento, mas também introduz riscos que precisam ser abordados.

Analisando o problema

Cada mensagem que os seus produtores enviam para uma partição Kafka possui um deslocamento – um número de índice sequencial que identifica cada mensagem. Para acompanhar quais mensagens já foram processadas, o seu consumidor precisa confirmar os deslocamentos das mensagens que foram processadas.


Deslocamentos confirmados em uma partição de tópico

Exceto que você esteja acionando confirmações manualmente, provavelmente você está usando o mecanismo de confirmação automática do consumidor do Kafka. A confirmação automática é habilitada imediatamente e, por padrão, faz confirmações a cada cinco segundos.

Para um serviço simples de transformação de dados, “processado” significa, simplesmente, que uma mensagem chegou e foi transformada, e depois produzida de volta para o Kafka. Para qualquer outro cenário, consideraríamos a mensagem como não processada. Isso é importante, porque se você consumir <InputMessage>, mas o seu serviço fechar antes de conseguir produzir <TransformedMessage>, você não deseja confirmar o deslocamento para essa mensagem de entrada—você precisa deixá-la ser coletada novamente.

Isso chega à raiz do nosso problema porque, infelizmente, não é assim que as confirmações automáticas funcionam. O consumidor do Kafka não tem ideia do que você faz com a mensagem, e é indiferente ao cometer compensações. No que diz respeito ao consumidor, assim que uma mensagem é recebida, ela é “processada”.

Então, agora imagine que o seu consumidor extraiu 1.000 mensagens, e as armazenou em buffer na memória. Em seguida, uma confirmação automática é acionada, confirmando as compensações para essas 1.000 mensagens. No entanto, digamos que o seu serviço tenha usado muita memória, e seja imediatamente encerrado por um sinal de interrupção do OOM, antes que todas as mensagens sejam processadas. Centenas dessas mensagens nunca serão processadas. Isso é perda de dados.

Eventos perdidos em uma partição de tópico

A situação inversa também é possível. Você poderia processar com êxito essas 1.000 mensagens e, em seguida, experimentar um desligamento forçado antes de confirmar as compensações. Nesse cenário, você reprocessará centenas de mensagens em outra instância, após o reequilíbrio dos consumidores. Isso é duplicação de dados.

Duplicação de dados em uma partição de tópico

Ok, então o que eu posso fazer sobre isso?

A má notícia é que não há soluções fáceis. Basicamente, este é um problema de garantias de consistência fracas. O que todos os usuários do Kafka desejam é o processamento exatamente uma vez—uma garantia de que você consumirá e processará mensagens ''exatamente uma vez''. O Kafka versão 0.11 tenta resolver esse problema, e tornou as coisas um pouco melhores. É possível escrever um pipeline ''exatamente uma vez'' com o Kafka 0.11, mas para fazer o consumo exatamente uma vez, você precisa implementar a sua própria semântica transacional em seus consumidores, para informar ao Kafka quando você concluiu o processamento (ou reverter, se algo der errado).

Além disso, em nossos testes, descobrimos que o modelo transacional para produção de mensagens no Kafka 0.11 não processava mensagens tão rapidamente quanto precisávamos, demorando de 10 a 100 milissegundos por mensagem. É muita latência adicional que não podemos incorrer.

Você tem algumas opções, no entanto. Várias soluções de streaming, como Flink, ou Kafka Streams, oferecem processamento exatamente uma vez, desde que você permaneça dentro das restrições dessas estruturas.

Outra opção seria “lançar a sua própria” estratégia exatamente uma vez, que comprometeria automaticamente as compensações apenas para mensagens que tivessem chegado ao final do pipeline de processamento. Esta é uma recomendação apenas para quem tem coragem, pois acertar é muito difícil, e pode causar mais problemas do que resolver.

E sempre há a opção de aceitar o risco de perda ou duplicação de dados. Infelizmente, esta é a opção escolhida por muitos usuários do Kafka, mas nem sempre é uma escolha sem fundamento. A quantidade de dados que você realmente perderia, ou duplicaria, em um desses cenários, é relativamente baixa; a confirmação automática deve estar a apenas alguns segundos da última mensagem confirmada. (Seus níveis completos são provavelmente o melhor indicador de quanta duplicação ou perda você veria). A frequência de ocorrência também deve ser baixa—idealmente, os seus serviços ao consumidor do Kafka não estão recebendo sinais regulares de interrupção do OOM.

Portanto, em comparação com o custo de engenharia para resolver esse problema em todos os seus serviços ao consumidor do Kafka, isso pode ser apenas um risco com o qual você terá que lidar se os dados com os quais você trabalha permitirem uma pequena perda. É claro que você sempre pode diminuir o risco criando serviços confiáveis."

Seguindo em frente

Como eu disse, temos muita experiência na New Relic gerenciando conjuntos Kafka. Descobrimos que construir serviços altamente confiáveis fica mais difícil conforme escalamos para lidar com grandes volumes de dados, e tomamos algumas decisões práticas sobre como lidar com essa falha específica no Kafka, caso um de nossos serviços sofra um desligamento forçado.Nos casos em que você aceita o risco de perda de dados, siga as seguintes etapas para minimizá-lo:

  • Esteja ciente deste problema, e documente-o na sua matriz de risco.
  • Certifique-se de que os seus serviços estejam estáveis. Fique atento com os SIGKILLs e OOMs do seu serviço.
  • Ao criar novos serviços, considere usar um sistema de streaming que resolva esse problema imediatamente.

Interessado em trabalhar com a New Relic? Leia sobre o nosso trabalho com o ZenHub para entender como alcançamos o sucesso."