No ambiente distribuído, a falha é um cenário muito comum que pode ocorrer a qualquer momento. No ambiente Kafka, o broker pode travar, falha na rede, falha no processamento, falha ao publicar mensagens ou falha no consumo de mensagens etc. Esse cenário diferente introduziu diferentes tipos de perda e duplicação de dados.
Cenários de falha
A (falha na confirmação ): o produtor publicou a mensagem com êxito com nova tentativa> 1, mas não pôde receber confirmação devido a falha. Nesse caso, o Producer tentará novamente a mesma mensagem pode apresentar duplicado.
B (processo do produtor falhou em mensagens em lote): produtor que enviou um lote de mensagens com falha, com pouco sucesso publicado. Nesse caso, e assim que o produtor reiniciar, republicará novamente todas as mensagens do lote que introduzirão duplicado no Kafka.
C (Falha ao ignorar e esquecer) O produtor publicou a mensagem com nova tentativa = 0 (acionar e esquecer). Em caso de falha publicada, não será informado e enviará a próxima mensagem, isso fará com que a mensagem seja perdida.
D (Falha no consumidor na mensagem em lote) Um consumidor recebe um lote de mensagens da Kafka e confirma manualmente seu deslocamento (enable.auto.commit = false). Se o consumidor falhar antes de se comprometer com Kafka, na próxima vez que o consumidor consumir os mesmos registros novamente, os quais serão reproduzidos em duplicado no lado do consumidor.
Semântica exatamente uma vez
Nesse caso, mesmo se um produtor tentar reenviar uma mensagem, isso fará com que a mensagem seja publicada e consumida pelo consumidor exatamente uma vez.
Para obter a semântica Exatamente uma vez em Kafka, ele usa abaixo de 3 propriedades
- enable.idempotence = true (endereço a, b & c)
- MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION = 5 (o produtor sempre terá uma solicitação a bordo por conexão)
- isolamento.level = read_committed (endereço d)
Habilitar Idempotent (enable.idempotence = true)
A entrega idempotente permite que o produtor grave uma mensagem no Kafka exatamente uma vez em uma partição específica de um tópico durante a vida útil de um único produtor, sem perda de dados e pedido por partição.
"Observe que ativar a idempotência exige que MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION seja menor ou igual a 5, RETRIES_CONFIG seja maior que 0 e ACKS_CONFIG seja 'todos'. Se esses valores não forem explicitamente definidos pelo usuário, serão escolhidos valores adequados. Se valores incompatíveis forem escolhidos. definido, uma ConfigException será lançada "
Para alcançar a idempotência, o Kafka usa uma identificação exclusiva, que é chamada de identificação do produto ou PID e número de sequência ao produzir mensagens. O produtor continua incrementando o número de sequência em cada mensagem publicada, que mapeia com PID exclusivo. O corretor sempre compara o número de sequência atual com o anterior e rejeita se o novo não for +1 maior que o anterior, o que evita a duplicação e, ao mesmo tempo, se mais do que o maior for perdido nas mensagens
No cenário de falha, o broker comparará o número de sequência com o anterior e, se a sequência não tiver aumentado, +1 rejeitará a mensagem.
Transação (isolamento.nível)
As transações nos permitem atualizar dados atomicamente em várias partições de tópicos. Todos os registros incluídos em uma transação serão salvos com sucesso, ou nenhum deles. Ele permite comprometer as compensações do consumidor na mesma transação, juntamente com os dados que você processou, permitindo, assim, a semântica de ponta a ponta exatamente uma vez. .
O Producer não espera para gravar a mensagem no kafka, onde o Producer usa beginTransaction, commitTransaction e abortTransaction (em caso de falha) O Consumidor usa isolation.level read_committed ou read_uncommitted
- read_committed: o consumidor sempre lerá apenas dados confirmados.
- read_uncommitted: leia todas as mensagens na ordem de compensação sem aguardar que as transações sejam confirmadas
Se um consumidor com isolated.level = read_committed alcançar uma mensagem de controle para uma transação que não foi concluída, ele não entregará mais mensagens dessa partição até que o produtor confirme ou anule a transação ou ocorra um tempo limite de transação. O tempo limite da transação é determinado pelo produtor usando a configuração transaction.timeout.ms (padrão 1 minuto).
Exatamente uma vez no produtor e consumidor
Em condições normais, onde temos produtores e consumidores separados. O produtor precisa idempotente e ao mesmo tempo gerenciar transações, para que o consumidor possa usar o isolamento.level para ler apenas read_committed para tornar todo o processo como operação atômica. Isso garante que o produtor sempre sincronize com o sistema de origem. Mesmo a interrupção ou transação do produtor interrompida, sempre será consistente e publique a mensagem ou lote de mensagens como unidade uma vez.
O mesmo consumidor receberá uma mensagem ou lote de mensagens como unidade uma vez.
No Produtor Semântico Exatamente Uma Vez, juntamente com o Consumidor, aparecerá como operação atômica que operará como uma unidade. Publique e seja consumido uma vez ou abortado.
Exatamente uma vez no Kafka Stream
O Kafka Stream consome mensagens do tópico A, processa e publica mensagem no Tópico B e, uma vez publicado, use commit (confirmação geralmente executada sob cobertura) para liberar todos os dados do armazenamento de estado no disco.
Exatamente uma vez no Kafka Stream é o padrão de leitura-processo-gravação que garante que essas operações sejam tratadas como operações atômicas. Como o Kafka Stream atende ao produtor, ao consumidor e às transações, o Kafka Stream vem com um processamento especial de parâmetros.
O Kafka Streams atualiza atomicamente as compensações do consumidor, as lojas locais do estado, os tópicos do log de alterações da loja do estado e a produção para exibir os tópicos todos juntos. Se qualquer uma dessas etapas falhar, todas as alterações serão revertidas.
processing.guarantee: exatamente_uma vez que forneça automaticamente os parâmetros abaixo que você não precisa definir explicitamente
- isolamento.level = read_committed
- enable.idempotence = true
- MAX_IN_FLIGHT_REQUESTS_PER_CONNECTION = 5