Como o Kafka lida com problemas de acesso simultâneo em implementações de armazenamento

1. Introdução

Kafka é uma fila de mensagens distribuídas que usa principalmente o sistema de arquivos para armazenar dados de mensagens, oferece suporte ao modo de publicação-assinatura e processa dados de streaming. Quando várias operações de armazenamento de tópicos e partições são executadas, ocorrerão problemas como acesso simultâneo e conflitos de dados.

2. Projeto do esquema de armazenamento Kafka

O design da solução de armazenamento Kafka requer a seleção de tecnologias relevantes e a implementação de mecanismos correspondentes para resolver os problemas de simultaneidade e bloqueio de várias operações de armazenamento de tópicos e partições. Ao mesmo tempo, é necessário otimizar a leitura e gravação do produtor e dados do consumidor.

2.1 Seleção de tecnologias relacionadas

2.1.1 Seleção do sistema de arquivos

Kafka usa sistemas de arquivos baseados em disco, como ext3, ext4, XFS, etc. Esses sistemas de arquivos são amplamente usados ​​em cenários de big data. Atualmente, a maioria dos armazenamentos de mensagens Kafka são baseados em sistemas de arquivos compatíveis com Linux, que suportam armazenamento persistente e recuperação rápida de dados.

2.1.2 Seleção do mecanismo de bloqueio

Tanto o tópico quanto a partição no Kafka podem ser escritos por vários produtores e lidos por vários consumidores ao mesmo tempo. Para resolver o problema de escrita simultânea de Tópico e Partição, o Kafka adota um mecanismo baseado em bloqueio de arquivo, ou seja, cada arquivo corresponde a um arquivo de bloqueio, e o arquivo de bloqueio tem o mesmo nome do arquivo de dados e o sufixo é ​.lock. O mecanismo de bloqueio pode ser usado para perceber que, quando vários produtores gravam em uma partição ao mesmo tempo, cada produtor obtém seu próprio bloqueio para garantir a gravação sequencial e a consistência dos dados.

2.2 Mecanismo de Implementação

2.2.1 Implementação de escrita sequencial e buffering

O Kafka usa um mecanismo baseado em gravação sequencial no design, que pode maximizar a taxa de transferência do disco e melhorar a eficiência da gravação de dados. Ao mesmo tempo, o Kafka também usa a tecnologia de buffer de memória para armazenar em cache os dados a serem gravados, reduzindo a frequência de gravação no disco. Quando certas condições são atendidas, os dados em cache são gravados no disco em lotes, o que pode melhorar significativamente o desempenho do Kafka.

2.2.2 Implementação de compressão e indexação

O Kafka oferece suporte à compactação e indexação de mensagens ao mesmo tempo. Por meio da compactação e da indexação, o volume geral de dados do Kafka é reduzido e os dados necessários podem ser localizados rapidamente durante o consumo, o que melhora a eficiência da leitura.

2.3 Otimização de leitura e gravação de dados do produtor/consumidor

Para otimizar a eficiência de leitura e gravação de dados de produtores e consumidores, Kafka adota soluções técnicas como cache de mensagens, tecnologia de cópia zero e partição de disco para otimização. Além disso, o Kafka também usa o processamento em lote para combinar vários dados em uma grande mensagem para envio, reduzindo o número de comunicações com o servidor e melhorando a eficiência da transmissão de dados.

// kafka 生产者生产消息示例
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

Producer<String, String> producer = new KafkaProducer<>(props);
for(int i = 0; i < 100; i++)
    producer.send(new ProducerRecord<>("test-topic", Integer.toString(i), Integer.toString(i)));
producer.close();

// kafka 消费者消费消息示例
Properties props = new Properties();
props.put("bootstrap.servers", "localhost:9092");
props.put("group.id", "test-group");
props.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
props.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Collections.singletonList("test-topic"));
while (true) {
    
    
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
    for (ConsumerRecord<String, String> record : records)
        System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
}

3. Prática do esquema de armazenamento Kafka

3.1 Detalhes e considerações de implementação

Ao usar o Kafka para armazenar dados, há vários pontos a serem observados:

  • Chave da mensagem e limite de tamanho do valor : por padrão, o tamanho da chave e do valor em uma mensagem Kafka pode ser de até 1 MB. Se precisar enviar mensagens maiores, será necessário modificar message.max.bytesos parâmetros e replica.fetch.max.bytesna configuração do intermediário e reiniciá-lo.
  • Armazenamento persistente de mensagens : as mensagens no Kafka são gravadas no disco de maneira anexada e, uma vez gravadas, não podem ser modificadas. Como a política de retenção de mensagens padrão do Kafka é a retenção de tempo, as mensagens são excluídas automaticamente. Se precisar armazenar mensagens persistentemente, você pode alterar a política de retenção de mensagens para reter por tamanho ou número de mensagens.
  • Garantia de confiabilidade da mensagem : Para garantir a confiabilidade da mensagem, ela pode ser configurada das seguintes formas:
    • acksParâmetros: Utilizados para especificar quais réplicas devem receber reconhecimentos para que uma mensagem seja considerada enviada com sucesso. São três valores: 0significa que não é necessária nenhuma confirmação; 1significa que precisa receber confirmação de pelo menos uma réplica; allsignifica que precisa aguardar a confirmação de todas as réplicas disponíveis.
    • retriesParâmetros: O número de vezes que uma mensagem pode ser repetida quando ocorre um erro de rede ou de partição. O padrão é 0, o que significa nenhuma repetição. Este parâmetro deve ser retry.backoff.msusado junto com o parâmetro para controlar o atraso entre as tentativas.
    • max.in.flight.requests.per.connectionParâmetro: É usado para especificar o número máximo de solicitações que o cliente pode enviar ao servidor antes de receber uma resposta do servidor. O padrão é 5, o que significa que Kafka pode enviar outras cinco solicitações antes de receber respostas para as primeiras cinco solicitações.

3.2 Teste de desempenho do sistema e esquema de otimização

No uso real, o desempenho da solução de armazenamento Kafka precisa ser testado e otimizado. A seguir estão vários testes de desempenho comuns e soluções de otimização:

  • Teste o desempenho simultâneo de leitura e gravação : teste seu desempenho de leitura e gravação enviando um grande número de mensagens simultâneas para o Kafka. num.io.threadsSe houver um gargalo de desempenho, considere aumentar o número de partições, aumentar os parâmetros e na configuração do agente num.network.threadse aumentar o tamanho do cluster Kafka para otimizar o desempenho.
  • Teste o desempenho da política de retenção de dados : teste o desempenho do Kafka definindo diferentes políticas de retenção de mensagens (por tempo, tamanho ou quantidade). Se houver um gargalo de armazenamento de dados, será necessário ajustar o parâmetro cleanup.policy ou reduzir a pressão de armazenamento de mensagens incluindo consumidores.
  • Teste a relação entre o tamanho da mensagem e o número e a taxa de transferência : teste o desempenho do Kafka enviando mensagens de diferentes números e tamanhos. De acordo com os resultados do teste, o tamanho e o número da mensagem podem ser otimizados para obter uma melhor taxa de transferência.

3.3 Atualização e atualização da solução de armazenamento

Quando a solução de armazenamento Kafka precisa ser atualizada ou atualizada, você precisa prestar atenção aos seguintes pontos:

  • Dados de backup : Antes de atualizar ou atualizar, todos os dados precisam ser copiados. É melhor fazer backup dos dados em um meio de armazenamento independente para evitar perda de dados.
  • Interrompendo serviços : ao atualizar o KafKa, você precisa interromper todos os serviços e garantir que nenhum serviço seja iniciado até que a atualização seja concluída.
  • Ordem de atualização : cada componente deve ser atualizado em ordem, por exemplo, primeiro atualize o Zookeeper, depois o corretor e, finalmente, o Kafka-client.
  • Teste e validação : após a conclusão da atualização, testes e validação são necessários para garantir que o sistema esteja funcionando corretamente e que nenhum dado tenha sido perdido.

4. Cenário de aplicação do problema de acesso simultâneo Kafka

Kafka é um sistema de enfileiramento de mensagens distribuído amplamente utilizado em cenários como processamento de fluxo de dados em tempo real, logs, eventos e métricas. Na implementação do armazenamento Kafka, ele adota alguns designs excelentes para resolver o problema de acesso simultâneo, portanto, também pode ser aplicado aos seguintes cenários.

4.1 Transações distribuídas baseadas em Kafka

O Kafka pode oferecer suporte a vários produtores para enviar mensagens ao mesmo tópico ao mesmo tempo e garantir a ordem das mensagens. Ao implementar transações distribuídas, o Kafka pode ser usado como um log de transações para registrar todas as operações relacionadas aos negócios. A exatidão e a confiabilidade das transações distribuídas podem ser garantidas usando os recursos idempotentes e atômicos do mecanismo de log de transações Kafka.

Aqui está um exemplo de código Java:

// 创建 Kafka 生产者
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
// 初始化事务
producer.initTransactions();
try {
    
    
    // 开启事务
    producer.beginTransaction();
    // 发送消息
    producer.send(new ProducerRecord<String, String>(topic, message));
    // 提交事务
    producer.commitTransaction();
} catch (ProducerFencedException | OutOfOrderSequenceException | AuthorizationException e) {
    
    
    // 异常处理
    producer.abortTransaction();
} catch (KafkaException e) {
    
    
    // 异常处理
    producer.abortTransaction();
}

4.2 Sistema de coleta de log em grande escala baseado em Kafka

Em um sistema de coleta de log em larga escala, uma grande quantidade de dados de log precisa ser processada e transmitida ao sistema de armazenamento em tempo real, o que requer processamento de fluxo de dados eficiente e transmissão de mensagem confiável. O Kafka pode ser usado como middleware para coleta de log, aceitando informações de log e distribuindo-as para a camada de armazenamento downstream, garantindo a confiabilidade da mensagem, sequência e mecanismo de repetição.

// 创建 Kafka 生产者
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
// 发送消息
producer.send(new ProducerRecord<String, String>(topic, message));

4.3 Sistema de processamento de fluxo baseado em Kafka

O Kafka também pode ser usado como infraestrutura de um sistema de processamento de fluxo, principalmente para funções como entrada, saída e transferência de dados. Os sistemas de processamento de fluxo coletam grandes quantidades de dados em tempo real, executam operações como cálculo, filtragem e agregação e, finalmente, enviam os resultados para sistemas de armazenamento downstream. O Kafka pode ser usado como uma camada de cache em um sistema de processamento de fluxo para garantir a pontualidade e a confiabilidade dos dados.

// 创建 Kafka 流处理器
StreamsBuilder builder = new StreamsBuilder();
// 输入源头 topic1
KStream<String, String> inputStream = builder.stream("topic1");
// 数据处理
KStream<String, String> outputStream = inputStream.filter((k, v) -> v.length() > 10);
// 输出目标 topic2
outputStream.to("topic2");
// 构建拓扑并启动应用
KafkaStreams streams = new KafkaStreams(builder.build(), props);
streams.start();

O código de exemplo acima demonstra como usar o Kafka para filtrar e gerar o fluxo de dados, filtrar os dados com um comprimento maior que 10 do tópico1 e enviá-los para o tópico2.

Acho que você gosta

Origin blog.csdn.net/u010349629/article/details/130935186
Recomendado
Clasificación