Kafka3.0.0版本——消费者(Sticky分区分配策略以及再平衡)

目录

一、Sticky分区分配策略原理

  • 粘性分区定义:可以理解为分配的结果带有"粘性的"。即在执行一次新的分配之前,考虑上一次分配的结果,尽量少的调整分配的变动,可以节省大量的开销。
  • 粘性分区是 Kafka 从 0.11.x 版本开始引入这种分配策略,首先会尽量均衡的放置分区到消费者上面,在出现同一消费者组内消费者出现问题的时候,会尽量保持原有分配的分区不变化。

二、Sticky分区分配策略 示例需求

  • 设置主题为 sevenTopic,7个分区;准备 3 个消费者,采用粘性分区策略,并进行消费,观察
    消费分配情况。然后再停止其中一个消费者,再次观察消费分配情况。

三、Sticky分区分配策略代码案例

3.1、创建带有7个分区的sevenTopic主题

  • 在 Kafka 集群控制台,创建带有7个分区的sevenTopic主题

    java 复制代码
    bin/kafka-topics.sh --bootstrap-server 192.168.136.27:9092 --create --partitions 7 --replication-factor 1 --topic sevenTopic

3.2、创建三个消费者 组成 消费者组

  • 复制 CustomConsumer1类,创建 CustomConsumer2和CustomConsumer3。这样可以由三个消费者组成消费者组,组名都为"test2",设置分区分配策略为 Sticky。

    java 复制代码
    package com.xz.kafka.consumer;
    
    import org.apache.kafka.clients.consumer.ConsumerConfig;
    import org.apache.kafka.clients.consumer.ConsumerRecord;
    import org.apache.kafka.clients.consumer.ConsumerRecords;
    import org.apache.kafka.clients.consumer.KafkaConsumer;
    import org.apache.kafka.common.serialization.StringDeserializer;
    
    import java.time.Duration;
    import java.util.ArrayList;
    import java.util.Properties;
    
    public class CustomConsumer1 {
    
        public static void main(String[] args) {
    
            // 0 配置
            Properties properties = new Properties();
    
            // 连接 bootstrap.servers
            properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.136.27:9092,192.168.136.28:9092,192.168.136.29:9092");
    
            // 反序列化
            properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
            properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
    
            // 配置消费者组id
            properties.put(ConsumerConfig.GROUP_ID_CONFIG,"test2");
            // 设置分区分配策略
            properties.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG,"org.apache.kafka.clients.consumer.StickyAssignor");
    
            // 1 创建一个消费者  "", "hello"
            KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<>(properties);
    
            // 2 订阅主题 sevenTopic
            ArrayList<String> topics = new ArrayList<>();
            topics.add("sevenTopic");
            kafkaConsumer.subscribe(topics);
    
            // 3 消费数据
            while (true){
    
                ConsumerRecords<String, String> consumerRecords = kafkaConsumer.poll(Duration.ofSeconds(1));
    
                for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
                    System.out.println(consumerRecord);
                }
            }
        }
    }

3.3、创建生产者

  • 创建CustomProducer生产者。

    java 复制代码
    package com.xz.kafka.producer;
    
    import org.apache.kafka.clients.producer.*;
    import org.apache.kafka.common.serialization.StringSerializer;
    import java.util.Properties;
    
    public class CustomProducerCallback {
    
        public static void main(String[] args) throws InterruptedException {
    
            //1、创建 kafka 生产者的配置对象
            Properties properties = new Properties();
    
            //2、给 kafka 配置对象添加配置信息:bootstrap.servers
            properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.136.27:9092,192.168.136.28:9092,192.168.136.29:9092");
    
            //3、指定对应的key和value的序列化类型 key.serializer value.serializer
            properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
            properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,StringSerializer.class.getName());
    
            //4、创建 kafka 生产者对象
            KafkaProducer<String, String> kafkaProducer = new KafkaProducer<>(properties);
    
            //5、调用 send 方法,发送消息
            for (int i = 0; i < 200; i++) {
                kafkaProducer.send(new ProducerRecord<>("sevenTopic", "hello kafka" + i), new Callback() {
                    @Override
                    public void onCompletion(RecordMetadata metadata, Exception exception) {
                        if (exception == null){
                            System.out.println("主题: "+metadata.topic() + " 分区: "+ metadata.partition());
                        }
                    }
                });
                Thread.sleep(2);
            }
    
            // 3 关闭资源
            kafkaProducer.close();
        }
    }

3.4、测试

  • 首先,在 IDEA中分别启动消费者1、消费者2和消费者3代码

  • 然后,在 IDEA中分别启动生产者代码

  • 在 IDEA 控制台观察消费者1、消费者2和消费者3控制台接收到的数据,如下图所示:


3.5、Sticky分区分配策略代码案例说明

  • 由上述测试输出结果截图可知: 消费者1消费0、2、4分区的数据;消费者2消费1、3分区的数据;消费者3消费5、6分区的数据。
  • 说明:Kafka 采用修改后的Sticky分区分配策略。

四、Sticky分区分配再平衡案例

4.1、停止某一个消费者后,(45s 以内)重新发送消息示例

  • 由下图控制台输出可知:2号消费者 消费到 2号分区数据。

  • 由下图控制台输出可知:3号消费者 消费到 0、4号分区数据。

4.2、停止某一个消费者后,(45s 以后)重新发送消息示例

  • 由下图控制台输出可知:2号消费者 消费到 1、2、3号分区数据。

  • 由下图控制台输出可知:3号消费者 消费到 0、4、5、6号分区数据。

4.3、Sticky分区分配再平衡案例说明

  • 1 号消费者的任务会按照粘性规则,尽可能均衡的随机分成 0 和 1 号分区数据,分别由 2号消费者或者 3号消费者消费。

  • 1号消费者挂掉后,消费者组需要按照超时时间 45s 来判断它是否退出,所以需要等待,时间到了 45s 后,判断它真的退出就会把任务分配给其他 broker 执行。

  • 消费者 1 已经被踢出消费者组,所以重新按照粘性方式分配。

相关推荐
智码看视界8 小时前
技术选型指南:Pulsar vs Kafka:消息队列选型终极对比与压测
kafka·消息队列·pulsar·消息中间件·流处理·高吞吐·架构选型
俊哥大数据9 小时前
Flink1.20.3 实时消费 Kafka 数据并解析入湖 Paimon1.4.2 全流程实战
分布式·flink·kafka·数据湖·paimon
今年下半年1 天前
记录一次IBMS物联网项目的架构设计及技术栈应用
物联网·websocket·网络协议·tcp/ip·微服务·udp·kafka
Nano叶落7 天前
Kafka 消费积压排查入门:Docker 搭环境亲手制造一次 ‘消息堵死‘,10 分钟看懂 Lag
kafka
吉甫作诵8 天前
Kafka 集群安装与运维实战:消费组排查、Offset 重置与副本重分配
大数据·运维·分布式·kafka·消息队列
imDwAaY8 天前
消息队列四大核心问题:顺序性、幂等性、可靠性与一致性
学习·kafka·rabbitmq
wno7049 天前
Spring Boot整合Kafka
spring boot·kafka
音符犹如代码9 天前
Kafka 接入 AI 的三条路线:MCP 提案、会话记忆与实时上下文
java·大数据·ai·kafka
happy_king_zi9 天前
kafka两种部署模式的区别
kafka·消息队列·sre
happy_king_zi9 天前
Kafka 3.9.2 KRaft 模式三节点集群完整部署指南
kafka·消息队列