A chave é necessária como parte do envio de mensagens para Kafka?

93
KeyedMessage<String, byte[]> keyedMessage = new KeyedMessage<String, byte[]>(request.getRequestTopicName(), SerializationUtils.serialize(message)); 
producer.send(keyedMessage);

Atualmente, estou enviando mensagens sem nenhuma chave como parte das mensagens codificadas. Ainda funcionará com delete.retention.ms? Preciso enviar uma chave como parte da mensagem? É bom tornar a chave como parte da mensagem?

Gaurav
fonte

Respostas:

172

As chaves são principalmente úteis / necessárias se você precisar de uma ordem forte para uma chave e estiver desenvolvendo algo como uma máquina de estado. Se você exigir que as mensagens com a mesma chave (por exemplo, um id exclusivo) sejam sempre vistas na ordem correta, anexar uma chave às mensagens garantirá que as mensagens com a mesma chave sempre vão para a mesma partição em um tópico. O Kafka garante a ordem dentro de uma partição, mas não entre as partições de um tópico, então, alternativamente, não fornecer uma chave - o que resultará na distribuição round-robin entre as partições - não manterá essa ordem.

No caso de uma máquina de estado, as chaves podem ser usadas com log.cleaner.enable para desduplicar entradas com a mesma chave. Nesse caso, Kafka assume que seu aplicativo só se preocupa com a instância mais recente de uma determinada chave e o limpador de log exclui duplicatas mais antigas de uma determinada chave apenas se a chave não for nula. Esta forma de compactação de log é controlada pela propriedade log.cleaner.delete.retention e requer chaves.

Como alternativa, a propriedade mais comum log.retention.hours , que é ativada por padrão, funciona excluindo segmentos completos do log que estão desatualizados. Neste caso, as chaves não precisam ser fornecidas. O Kafka simplesmente excluirá partes do log mais antigas do que o período de retenção fornecido.

Isso é tudo para dizer, se você habilitou a compactação de log ou requer ordem estrita para mensagens com a mesma chave, então você definitivamente deve usar chaves. Caso contrário, as chaves nulas podem fornecer melhor distribuição e evitar possíveis problemas de hot spotting nos casos em que algumas chaves podem aparecer mais do que outras.

Kuujo
fonte
Eu sou novo no Kafka, essa é a razão de fazer tantas perguntas: Existem algumas perguntas sobre isso: Primeira pergunta, podemos consumir a mensagem com base na chave, Atualmente estou consumindo a mensagem de MessagAndMetadata mm. ou não há problema em ignorar a chave no momento de consumir a mensagem. Estou usando uma API de consumidor de alto nível.
gaurav
1
@kuujo Estou assumindo que essa eliminação de duplicação é apenas para entradas de registro, não necessariamente elimina a duplicação de mensagens em uma fila de tópicos.
user1658296
2
@oblivion fazer com que as mensagens vão para a mesma partição sequencialmente é importante para lidar com atualizações não idemponentes, por exemplo, o cliente seleciona a data de entrega (uma mensagem), mas muda de ideia mais tarde (segunda mensagem). Se as mensagens fossem para partições diferentes, qualquer uma das mensagens pode ser processada primeiro / último, por exemplo, com 2 consumidores consumindo de cada partição. Se as duas mensagens relacionadas à mesma entrega forem para a mesma partição, elas serão processadas primeiro a entrar, primeiro a sair, fornecendo a data final de entrega correta.
Kunal
3
As garantias de pedido não vêm da chave, mas das mensagens que estão na mesma partição. O roteamento de mensagens para partições não precisa ser baseado em chave. Você pode especificar explicitamente uma partição ao criar umProducerRecord
Malte de
2
Meu entendimento é que o cliente produtor é o responsável pela escolha da partição ( kafka.apache.org/documentation.html#design_loadbalancing ), que pode ou não ser baseada na chave. Então, por que você diz que as chaves são necessárias para fazer o pedido?
lfk
5

Além da resposta aceita muito útil, gostaria de adicionar mais alguns detalhes

Particionamento

Por padrão, o Kafka usa a chave da mensagem para selecionar a partição do tópico em que grava. Isso é feito por algo como

hash(key) % number_of_partitions

Se nenhuma chave for fornecida, o Kafka irá particionar os dados aleatoriamente em rodízio.

Encomenda

Conforme afirmado na resposta fornecida, Kafka tem garantias sobre a ordem das mensagens apenas no nível de partição.

Digamos que você queira armazenar transações financeiras para seus clientes em um tópico Kafka com duas partições. As mensagens podem ser semelhantes a (chave: valor)

null:{"customerId": 1, "changeInBankAccount": +200}
null:{"customerId": 2, "changeInBankAccount": +100}
null:{"customerId": 1, "changeInBankAccount": +200}
null:{"customerId": 1, "changeInBankAccount": -1337}
null:{"customerId": 1, "changeInBankAccount": +200}

Como não definimos uma chave, as duas partições provavelmente se parecerão

// partition 0
null:{"customerId": 1, "changeInBankAccount": +200}
null:{"customerId": 1, "changeInBankAccount": +200}
null:{"customerId": 1, "changeInBankAccount": +200}

// partition 1
null:{"customerId": 2, "changeInBankAccount": +100}
null:{"customerId": 1, "changeInBankAccount": -1337}

O seu consumidor ao ler esse tópico pode acabar dizendo que o saldo da conta é 600 em um determinado momento, embora esse nunca tenha sido o caso! Só porque estava lendo todas as mensagens na partição 0 antes das mensagens na partição 1.

Com uma chave sensata (como customerId), isso poderia ser evitado, pois o particionamento seria assim:

// partition 0
1:{"customerId": 1, "changeInBankAccount": +200}
1:{"customerId": 1, "changeInBankAccount": +200}
1:{"customerId": 1, "changeInBankAccount": -1337}
1:{"customerId": 1, "changeInBankAccount": +200}

// partition 1
2:{"customerId": 2, "changeInBankAccount": +100}

Compactação de toras

Sem uma chave como parte de suas mensagens, você não poderá definir a configuração do tópico cleanup.policycomo compacted. De acordo com a documentação, "a compactação do log garante que o Kafka sempre manterá pelo menos o último valor conhecido para cada chave de mensagem dentro do log de dados para uma única partição de tópico.".

Esta configuração agradável e útil não estará disponível sem qualquer chave.

Uso de chaves

Em casos de uso da vida real, a chave de uma mensagem Kafka pode ter uma grande influência em seu desempenho e clareza de sua lógica de negócios.

Uma chave pode, por exemplo, ser usada naturalmente para particionar seus dados. Como você pode controlar seus consumidores para lerem de partições específicas, isso pode servir como um filtro eficiente. Além disso, a chave pode incluir alguns metadados no valor real da mensagem que ajuda a controlar o processamento subsequente. As chaves são geralmente menores do que os valores e, portanto, é mais conveniente analisar uma chave em vez de todo o valor. Ao mesmo tempo, você pode aplicar todas as serializações e registro de esquema como feito com seu valor também com a chave.

Como observação, existe também o conceito de Cabeçalho que pode ser usado para armazenar informações, consulte a documentação .

Mike
fonte