O código de integração do grupo de consumidores Kafka implementa a estratégia de alocação de partições e cenários de uso de compensação

Grupo de Consumidores (CG ): Um grupo de consumidores que consiste em vários consumidores. A condição para formar um grupo de consumidores é que todos os consumidores tenham o mesmo groupid.

• Cada consumidor do grupo de consumidores é responsável por consumir dados de diferentes partições e uma partição só pode ser consumida por um consumidor do grupo.

• Os grupos de consumidores não afetam uns aos outros. Todos os consumidores pertencem a um determinado grupo de consumidores, ou seja, um grupo de consumidores é logicamente um assinante.

Processo de inicialização do grupo de consumidores

1. Coordenador: Auxilia na inicialização de grupos de consumidores e na alocação de partições

seleção do nó coordenador = valor do hashcode do groupid % 50 (número de partições para __consumer_offsets)

1) Cada consumidor envia uma solicitação JoinGroup

2) Selecione um consumidor como líder

3) Envie o tópico a ser consumido para o consumidor líder

4) O líder será responsável por formular planos de consumo

5) Envie o plano de consumo para o coordenador

6) O Coordenador envia o plano de consumo para cada consumidor

7) Cada consumidor manterá uma pulsação com o coordenador (padrão 3s), uma vez esgotado o tempo limite (session.timeout.ms=45s), o consumidor será removido e o rebalanceamento será acionado; ou o consumidor demora muito para processar as mensagens ( max.poll.interval.ms5min), também aciona o rebalanceamento

Observação: o ID do grupo de consumidores deve ser configurado no código da API do consumidor. Se o consumidor for iniciado a partir da linha de comando sem preencher o ID do grupo de consumidores, um ID aleatório do grupo de consumidores será preenchido automaticamente

Código

public class CustomConsumer {
     public static void main(String[] args) {
         // 1.创建消费者的配置对象
         Properties properties = new Properties();
         // 2.给消费者配置对象添加参数
         properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.6.100:9092");
         // 配置序列化 必须
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG,
        StringDeserializer.class.getName());

        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, 
        StringDeserializer.class.getName());
         // 配置消费者组(组名任意起名) 必须
         properties.put(ConsumerConfig.GROUP_ID_CONFIG, "test");
         // 创建消费者对象
         KafkaConsumer<String, String> kafkaConsumer = new 
        KafkaConsumer<String, String>(properties);
         // 注册要消费的主题(可以消费多个主题)
         ArrayList<String> topics = new ArrayList<>();
         topics.add("first");
         kafkaConsumer.subscribe(topics);
         // 拉取数据打印
         while (true) {
         // 设置 1s 中消费一批数据
         ConsumerRecords<String, String> consumerRecords = 
        kafkaConsumer.poll(Duration.ofSeconds(1));
         // 打印消费到的数据
         for (ConsumerRecord<String, String> consumerRecord : 
        consumerRecords) {
         System.out.println(consumerRecord);
         }
         }
     }
}

Designar para consumir uma determinada partição

public class CustomConsumer {
     public static void main(String[] args) {
         // 1.创建消费者的配置对象
         Properties properties = new Properties();
         // 2.给消费者配置对象添加参数
         properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.6.100:9092");
         // 配置序列化 必须
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, 
        StringDeserializer.class.getName());

        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,
        StringDeserializer.class.getName());
         // 配置消费者组(必须),名字可以任意起
         properties.put(ConsumerConfig.GROUP_ID_CONFIG,"test");
         KafkaConsumer<String, String> kafkaConsumer = new 
        KafkaConsumer<>(properties);
         // 消费某个主题的某个分区数据
         ArrayList<TopicPartition> topicPartitions = new 
        ArrayList<>();
         topicPartitions.add(new TopicPartition("first", 0));
         kafkaConsumer.assign(topicPartitions);
         while (true){
         ConsumerRecords<String, String> consumerRecords = 
        kafkaConsumer.poll(Duration.ofSeconds(1));
         for (ConsumerRecord<String, String> consumerRecord : 
        consumerRecords) {
         System.out.println(consumerRecord);
         }
        }
   }
}

Estratégia de alocação de partição

Quando vários consumidores começam a consumir, os consumidores consumirão automaticamente em partições e cada consumidor consumirá o conteúdo da partição correspondente

Kafka tem quatro estratégias principais de alocação de partições: Range, RoundRobin, Sticky e CooperativeSticky. A estratégia de atribuição de partição pode ser modificada configurando o parâmetro partition.assignment.strategy. A estratégia padrão é Range + CooperativeSticky. Kafka pode usar várias estratégias de alocação de partições ao mesmo tempo

O intervalo é para cada tópico. Primeiro, as partições no mesmo tópico são classificadas por número de série e os consumidores são classificados em ordem alfabética

Se houver 7 partições e 3 consumidores agora, as partições classificadas serão 0,1,2,3,4,5,6; depois que os consumidores forem classificados, eles serão C0,C1,C2, através do número de partições / contagem de consumidores para determinar quantas partições cada consumidor deve consumir. Caso contrário, os primeiros consumidores consumirão mais 1 partição.

RoundRobin e rebalanceamento

A estratégia de partição de polling RoundRobin é listar todas as partições e todos os consumidores, classificá-los de acordo com o hashcode e, finalmente, alocar partições para cada consumidor por meio do algoritmo de polling.

Reequilibrar :

Pare o consumidor 0 e reenvie a mensagem (dentro de 45s)

A tarefa do consumidor nº 0 dividirá a pesquisa de dados em diferentes áreas de acordo com o método RoundRobin, que será executado por outros consumidores

(Após 45s) O consumidor 0 foi expulso do grupo de consumidores

// Modifica a estratégia de atribuição de partição
properties.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFI
G, "org.apache.kafka.clients.consumer.RoundRobinAssignor");

Pegajoso e reequilibrado

Sticky partition é um tipo de estratégia de alocação introduzida pelo Kafka desde a versão 0.11.x. Primeiro, as partições serão colocadas nos consumidores da forma mais uniforme possível. Quando houver um problema com consumidores no mesmo grupo de consumidores, a partição alocada original será mantida tanto quanto possível, não muda.

Semelhante ao intervalo, mas as partições adesivas são aleatórias

// Modifica a estratégia de alocação de partição
ArrayList<String> startegys = new ArrayList<>();
startegys.add("org.apache.kafka.clients.consumer.StickyAssignor");
properties.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG, 
startegys);

deslocamento de compensação

O offset existe no tópico do sistema para evitar interação com zookeeper, o que traz muita comunicação de rede

Visualize o conteúdo do tópico do sistema, adicione a configuração exclude.internal.topics=false no arquivo de configuração config/consumer.properties,

O padrão é true, o que significa que os temas do sistema não podem ser consumidos. Para visualizar os dados do tema do sistema, este parâmetro é modificado para false

consumidor

bin/kafka-console-consumer.sh --bootstrap-server 192.168.100:9092 --topic test --group test

Veja o tópico de consumo do consumidor __consumer_offsets

bin/kafka-console-consumer.sh --topic 
__consumer_offsets --bootstrap-server 192.168.100:9092192.168.100:9092 --consumer.config config/consumer.properties --formatter 
"kafka.coordinator.group.GroupMetadataManager\$ OffsetsMessageForm
atter" --desde-início

O tópico __consumer_offsets usa chave e valor para armazenar dados. A chave é group.id+topic+ número da partição e o valor é o valor de deslocamento atual. De vez em quando, o Kafka compactará esse tópico internamente, ou seja, cada group.id+topic+partition number manterá os dados mais recentes

Comprometer compensações automaticamente

enable.auto.commit: se a função de deslocamento de confirmação automática deve ser ativada, o padrão é true

auto.commit.interval.ms: O intervalo de tempo para confirmação automática de deslocamentos, o padrão é 5s

// Se deve enviar automaticamente o deslocamento
 properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, 
true);
 //O período de tempo para enviar o deslocamento é 1000ms, o padrão é 5s
properties.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, 
1000);

Confirme manualmente o deslocamento

Há duas maneiras de confirmar manualmente os deslocamentos: commitSync (commit síncrono) e commitAsync (commit assíncrono). O mesmo ponto entre os dois é que o deslocamento mais alto do lote de dados enviado desta vez será enviado; a diferença é que o envio síncrono bloqueia o encadeamento atual até que o envio seja bem-sucedido e falhará automaticamente e tentará novamente; enquanto o envio assíncrono não not Falha ao tentar novamente mecanismo, por isso é possível enviar falhou.

• commitSync (commit síncrono): Você deve esperar que o deslocamento seja confirmado antes de consumir o próximo lote de dados.

• commitAsync (commit assíncrono): Após enviar a solicitação de compensação de commit, comece a consumir o próximo lote de dados.

Confirmar deslocamento de forma síncrona

public class CustomConsumerByHandSync {
     public static void main(String[] args) {
         // 1. 创建 kafka 消费者配置类
         Properties properties = new Properties();
         // 2. 添加配置参数
         // 添加连接
         properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, 
        "192.168.6.100:9092");
         // 配置序列化 必须
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, 
        "org.apache.kafka.common.serialization.StringDeserializer");

        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, 
        "org.apache.kafka.common.serialization.StringDeserializer");
         // 配置消费者组
        properties.put(ConsumerConfig.GROUP_ID_CONFIG, "test");
         // 是否自动提交 offset
         properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, 
        false);
         //3. 创建 kafka 消费者
         KafkaConsumer<String, String> consumer = new 
        KafkaConsumer<>(properties);
         //4. 设置消费主题 形参是列表
         consumer.subscribe(Arrays.asList("first"));
         //5. 消费数据
         while (true){
         // 读取消息
         ConsumerRecords<String, String> consumerRecords = 
        consumer.poll(Duration.ofSeconds(1));
         // 输出消息
         for (ConsumerRecord<String, String> consumerRecord : 
        consumerRecords) {
         System.out.println(consumerRecord.value());
         }
         // 同步提交 offset
         consumer.commitSync();
         }
     }
}

Confirmar deslocamento de forma assíncrona

// 异步提交 offset
 consumer.commitAsync();

Especificar o consumo de compensação

auto.offset.reset = mais antigo | mais recente | nenhum O padrão é mais recente

O que fazer quando não há deslocamento inicial no Kafka (primeiro consumo pelo grupo de consumidores) ou quando o deslocamento atual não existe mais no servidor (por exemplo, os dados foram excluídos)?

(1) mais cedo: redefine automaticamente o deslocamento para o deslocamento mais antigo, --desde-início.

(2) mais recente (padrão): Redefinir automaticamente o deslocamento para o deslocamento mais recente.

(3) nenhum: se o deslocamento anterior do grupo de consumidores não for encontrado, lançar uma exceção para o consumidor

Especifique arbitrariamente o deslocamento de deslocamento para iniciar o consumo

public class CustomConsumerSeek {
     public static void main(String[] args) {
         // 0 配置信息
             Properties properties = new Properties();
             // 连接
             properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, 
            "192.168.6.100:9092");
             // key value 反序列化

            properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, 
            StringDeserializer.class.getName());

            properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, 
            StringDeserializer.class.getName());
             properties.put(ConsumerConfig.GROUP_ID_CONFIG, "test2");
             // 1 创建一个消费者
             KafkaConsumer<String, String> kafkaConsumer = new 
            KafkaConsumer<>(properties);
            // 2 订阅一个主题
             ArrayList<String> topics = new ArrayList<>();
             topics.add("first");
             kafkaConsumer.subscribe(topics);
             Set<TopicPartition> assignment= new HashSet<>();
             while (assignment.size() == 0) {
             kafkaConsumer.poll(Duration.ofSeconds(1));
             // 获取消费者分区分配信息(有了分区分配信息才能开始消费)
             assignment = kafkaConsumer.assignment();
             }
             // 遍历所有分区,并指定 offset 从 1700 的位置开始消费
             for (TopicPartition tp: assignment) {
             kafkaConsumer.seek(tp, 1700);
             }
             // 3 消费该主题数据
             while (true) {
             ConsumerRecords<String, String> consumerRecords = 
            kafkaConsumer.poll(Duration.ofSeconds(1));
             for (ConsumerRecord<String, String> consumerRecord : 
            consumerRecords) {
             System.out.println(consumerRecord);
             }
         }
     }
}

Nota: Após cada execução, o nome do grupo de consumidores precisa ser modificado;

Consumo de tempo especificado

Demanda: No ambiente de produção, você encontrará dados anormais nas últimas horas de consumo e deseja voltar a consumir de acordo com o tempo. Por exemplo, se for necessário consumir os dados do dia anterior de acordo com o horário, como lidar com isso?

public class CustomConsumerForTime {
     public static void main(String[] args) {
         // 0 配置信息
        Properties properties = new Properties();
         // 连接
         properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, 
        "192.168.6.100:9092");
         // key value 反序列化

        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, 
        StringDeserializer.class.getName());

        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, 
        StringDeserializer.class.getName());
         properties.put(ConsumerConfig.GROUP_ID_CONFIG, "test2");
         // 1 创建一个消费者
         KafkaConsumer<String, String> kafkaConsumer = new 
        KafkaConsumer<>(properties);
         // 2 订阅一个主题
         ArrayList<String> topics = new ArrayList<>();
         topics.add("first");
         kafkaConsumer.subscribe(topics);
         Set<TopicPartition> assignment = new HashSet<>();
         while (assignment.size() == 0) {
         kafkaConsumer.poll(Duration.ofSeconds(1));
         // 获取消费者分区分配信息(有了分区分配信息才能开始消费)
         assignment = kafkaConsumer.assignment();
         }
         HashMap<TopicPartition, Long> timestampToSearch = new 
        HashMap<>();
         // 封装集合存储,每个分区对应一天前的数据
         for (TopicPartition topicPartition : assignment) {
         timestampToSearch.put(topicPartition, 
        System.currentTimeMillis() - 1 * 24 * 3600 * 1000);
         }
         // 获取从 1 天前开始消费的每个分区的 offset
         Map<TopicPartition, OffsetAndTimestamp> offsets = 
        kafkaConsumer.offsetsForTimes(timestampToSearch);
         // 遍历每个分区,对每个分区设置消费时间。
         for (TopicPartition topicPartition : assignment) {
         OffsetAndTimestamp offsetAndTimestamp = 
        offsets.get(topicPartition);
         // 根据时间指定开始消费的位置
         if (offsetAndTimestamp != null){
         kafkaConsumer.seek(topicPartition, 
        offsetAndTimestamp.offset());
         }
     }
    // 3 消费该主题数据
     while (true) {
         ConsumerRecords<String, String> consumerRecords = 
        kafkaConsumer.poll(Duration.ofSeconds(1));
         for (ConsumerRecord<String, String> consumerRecord : 
        consumerRecords) {
         System.out.println(consumerRecord);
         }
         }
     }
}

Consumo em falta e consumo duplo

Consumo repetido: Os dados foram consumidos, mas a compensação não foi enviada. Consumo ausente: Enviar o deslocamento primeiro e depois consumi-lo pode resultar em consumo ausente de dados.

Cenário 1: Repetir o consumo. Comprometer automaticamente o deslocamento causado por

1) Consumidor envia compensação a cada 5 segundos

2) Se 2s após o envio do offset, o consumidor trava

3) Reinicie o consumidor novamente e continue consumindo a partir do deslocamento enviado pela última vez, resultando em consumo repetido

consumo perdido

Defina o deslocamento para envio manual. Quando o deslocamento é enviado, os dados ainda estão na memória e não foram colocados no disco. Neste momento, o encadeamento do consumidor é eliminado, o deslocamento foi enviado, mas os dados foram não foi processado, resultando na perda de dados nesta parte da memória.

assuntos do consumidor

Se você deseja concluir o consumo único preciso no lado do consumidor, precisa vincular atomicamente o processo de consumo e o processo de envio de compensação no lado do consumidor Kafka. Neste ponto, precisamos salvar o deslocamento do Kafka em uma mídia personalizada que suporte transações

acúmulo de dados

1) Se a capacidade de consumo do Kafka for insuficiente, você pode considerar aumentar o número de partições do tópico e, ao mesmo tempo, aumentar o número de consumidores no grupo de consumidores, o número de consumidores = o número de partições

2) Se o processamento de dados downstream não for oportuno: aumente o número de pulls por lote. Poucos dados são extraídos em lotes (puxando dados/tempo de processamento < velocidade de produção), para que os dados processados ​​sejam menores que os dados de produção, o que também causará atrasos de dados

Acho que você gosta

Origin blog.csdn.net/weixin_52210557/article/details/123580929
Recomendado
Clasificación