kafka消费者

1.Kafka消费者

1.1 kafka消费方式

消息队列的两种消费方式

  • pull(拉)模式
    consumer采用从broker中主动拉取数据。Kafka采用这种方式
  • push(推)模式
    broker主动将消息推给consumer

一、为什么Kafka没有采用push模式?
因为在push模式下, broker决定消息的发送速率, 很难使用所有消费者的消费速率,例如推送的速度是50m/s,Consumer1、Consumer2就来不及处理消息。

在这里插入图片描述

二、pull的不足之处
pull模式不足之处是,如果Kafka没有数据,消费者可能会陷入循环中,一直返回空数据。


1.2 Kafka消费者工作流程

1.2.1 消费者总体工作流程

生产者工作流程:

  1. 生产者向每一个分区的leader发送消息(一批一批发送, 触发Deque阈值)
  2. follower主动跟leader进行数据同步, 保证数据的可靠性

在这里插入图片描述

一个消费者可以消费多个分区的数据, 例如consumer可以消费topicA下的partition0、partition1、partition2


1.2.2 消费者组原理

消费者组(consumer group), 简称CG, 由多个消费者构成

一、消费者组形成的条件
形成一个消费者组的条件,是所有消费者的groupid相同。

  • 消费者组中每个消费者负责消费不同分区的数据, 一个分区只能由一个组内的消费者消费, 例如有两个消费者, 但是有4个分区, 那么两个消费者会分别消费其中两个分区
  • 消费者组之间互不影响, 所有的消费者都属于某个消费者组, 消费者组是逻辑上的一个订阅者

在这里插入图片描述
如果向消费组中添加更多的消费者, 超过主题分区数量(组中消费者数量 > 主题分区数), 则有一部分消费者会闲置, 不会接收任何消息

消费者组之间互不影响, 所有的消费者都属于某个消费者组, 即消费者组是逻辑上的一个订阅者
在这里插入图片描述


1.2.3 消费者组初始化流程

一、coordinator
辅助实现消费者组的初始化和分区的分配
coordinator节点选择 = groupid的hashcode值 % 50( __consumer_offsets的分区数量)

例如: groupid的hashcode值 = 1,1% 50 = 1,那么__consumer_offsets 主题的1号分区,在哪个broker上,就选择这个节点的coordinator作为这个消费者组的老大。消费者组下的所有的消费者提交offset的时候就往这个分区去提交offset。

  1. kafka集群根据consumer组的groupId计算出选择哪个broker的coordinator作为消费者组初始化以及分区分配的协调者
  2. 组中所有消费者都会主动向coordinator发送joinGroup请求. 然后从所有消费者中选出一个leader
  3. 将要消费的topic的情况发送给leader消费者
  4. leader消费者负责制定消费策略
  5. 将消费策略发送给coordinator
  6. coordinator把消费方案下发给所有consumer
  7. 每个消费者都会和coordinator保持心跳(默认3s), 一旦超时(session.timeout.ms=45s),该消费者会被移除,并触发再平衡;或者消费者处理消息的时间过长(max.poll.interval.ms5分钟),也会触发再平衡

1.2.4 消费者组详细消费流程

在这里插入图片描述


1.3 消费者 API

1.3.1 独立消费者案例(订阅主题)

创建一个独立消费者, 消费First主题中的数据
在这里插入图片描述

注意点:在消费者API中必须配置消费组id(groupId), 命令行启动消费者时不填写消费者id, 会自动填写随机的消费组id

一、通过java实现consumer

public class CustomConsumer {
    public static void main(String[] args) {
        // 1.创建消费者的配置对象
        Properties properties = new Properties();

        // 2.给消费者配置对象添加参数
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.202.130:9092");

        // 3.配置序列化(必须)
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());

        // 4.配置消费者组(组名任意起名) 必须
        properties.put(ConsumerConfig.GROUP_ID_CONFIG, "test");

        // 5.创建消费者对象
        KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<String, String>(properties);

        // 6.注册要消费的主题(可以消费多个主题)
        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);
            }
        }
    }
}

二、在Kafka集群控制台,创建Kafka生产者,并输入数据

bin/kafka-console-producer.sh --bootstrap-server 192.168.202.130:9092 --topic first
> hello

三、在IDEA控制台观察收到的数据

ConsumerRecord(topic = first, partition = 1, leaderEpoch = 6, offset = 36, CreateTime = 1679573070488, serialized key size = -1, serialized value size = 5, headers = RecordHeaders(headers = [], isReadOnly = false), key = null, value = hello)

1.3.2 独立消费者案例(订阅分区)

创建一个独立消费者,消费 first 主题 0 号分区的数据。
在这里插入图片描述

一、通过java实现consumer

public class CustomConsumerPartition {
    public static void main(String[] args) {
        // 1.创建消费者的配置对象
        Properties properties = new Properties();

        // 2.给消费者配置对象添加参数
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.202.130:9092");

        // 3.配置序列化(必须)
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());

        // 4.配置消费者组(必须), 名字可以任意起
        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);
            }
        }
    }
}

二、在 IDEA 中执行生产者程序 CustomProducerCallback()在控制台观察生成几个 0 号分区的数据。

first 0 381
first 0 382
first 2 168
first 1 165
first 1 166

三、在 IDEA 控制台,观察接收到的数据,只能消费到 0 号分区数据表示正确

ConsumerRecord(topic = first, partition = 0, leaderEpoch = 14, offset = 381, CreateTime = 1636791331386, serialized key size = -1, serialized value size = 9, headers = RecordHeaders(headers = [], isReadOnly = false), key = null, value = atguigu 0)

ConsumerRecord(topic = first, partition = 0, leaderEpoch = 14, offset = 382, CreateTime = 1636791331397, serialized key size = -1, serialized value size = 9, headers = RecordHeaders(headers = [], isReadOnly = false), key = null, value = atguigu 1)

1.3.3 消费者组案例

测试同一个主题的分区数据,只能由一个消费者组中的一个消费。
在这里插入图片描述

一、复制一份CustomConsumer, 在IDEA中同时启动
两个消费者的groupId需要一致

二、启动代码中的生产者发送消息

主题:first->分区:2
主题:first->分区:2
主题:first->分区:1
主题:first->分区:1
主题:first->分区:2

三、在 IDEA 控制台即可看到两个消费者在消费不同分区的数据
1.CustomConsumer1

ConsumerRecord(topic = first, partition = 1, leaderEpoch = 6, offset = 48, CreateTime = 1679574836068, serialized key size = -1, serialized value size = 9, headers = RecordHeaders(headers = [], isReadOnly = false), key = null, value = atguigu 2)
ConsumerRecord(topic = first, partition = 1, leaderEpoch = 6, offset = 49, CreateTime = 1679574836072, serialized key size = -1, serialized value size = 9, headers = RecordHeaders(headers = [], isReadOnly = false), key = null, value = atguigu 3)

2.CustomConsumer2

ConsumerRecord(topic = first, partition = 2, leaderEpoch = 8, offset = 22, CreateTime = 1679574836054, serialized key size = -1, serialized value size = 9, headers = RecordHeaders(headers = [], isReadOnly = false), key = null, value = atguigu 0)
ConsumerRecord(topic = first, partition = 2, leaderEpoch = 8, offset = 23, CreateTime = 1679574836065, serialized key size = -1, serialized value size = 9, headers = RecordHeaders(headers = [], isReadOnly = false), key = null, value = atguigu 1)
ConsumerRecord(topic = first, partition = 2, leaderEpoch = 8, offset = 24, CreateTime = 1679574836076, serialized key size = -1, serialized value size = 9, headers = RecordHeaders(headers = [], isReadOnly = false), key = null, value = atguigu 4)

1.4 生产经验—分区的分配以及再平衡

  1. 一个consumer group中有多个consumer组成,一个 topic有多个partition组成,现在的问题是,到底由哪个consumer来消费哪个partition的数据。
  2. Kafka有四种主流的分区分配策略
    • Range
    • RoundRobin
    • Sticky
    • CooperativeSticky

可以通过配置参数partition.assignment.strategy,修改分区的分配策略。默认策略是Range + CooperativeSticky。Kafka可以同时使用多个分区分配策略。


1.4.1 Range 以及再平衡(todo)

Range是对每个topic而言的

  1. 首先对同一个 topic 里面的分区按照序号进行排序,并对消费者按照字母顺序进行排序
    假如现在有 7 个分区,3 个消费者,排序后的分区将会是0,1,2,3,4,5,6;消费者排序完之后将会是C0,C1,C2。

  2. 通过 partitions数/consumer数 来决定每个消费者应该消费几个分区。如果除不尽,那么前面几个消费者将会多消费 1 个分区。
    例如,7/3 = 2 余 1 ,除不尽,那么 消费者 C0 便会多消费 1 个分区。 8/3=2余2,除不尽,那么C0和C1分别多消费一个。

在这里插入图片描述
注意:如果只是针对 1 个 topic 而言,C0消费者多消费1个分区影响不是很大。但是如果有 N 多个 topic,那么针对每个 topic,消费者 C0都将多消费 1 个分区,topic越多,C0消费的分区会比其他消费者明显多消费 N 个分区。

容易产生数据倾斜!


1.4.2 RoundRobin 以及再平衡(todo)

1.4.3 Sticky 以及再平衡(todo)

1.5 offset 位移

1.5.1 offset 的默认维护位置(todo)

1.5.2 自动提交 offset

1.5.3 手动提交 offset

1.5.4 指定 Offset 消费

1.5.5 指定时间消费

1.5.6 漏消费和重复消费

1.6 生产经验—消费者事务

1.7 生产经验—数据积压(消费者如何提高吞吐量)

2.Kafka-Eagle 监控

2.1 MySQL 环境准备

2.2 Kafka 环境准备

2.3 Kafka-Eagle 安装

2.4 Kafka-Eagle 页面操作

3.Kafka-Kraft 模式

3.1 Kafka-Kraft 架构

3.2 Kafka-Kraft 集群部署

3.3 Kafka-Kraft 集群启动停止脚本

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值