kafka基础入门(4):kafka消费者

kafka消费者

kafka消费方式

kafka采用pull(拉)模式,consumer从broker中拉取数据

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

kafka不采用push(推)模式,因为由broker决定消息发送速率,很难适应所有消费者的消费速率

Kafka 消费者工作流程

offset记录的是当前消费者消费到了哪一条数据,在系统主题中持久化存储可以使得消费者重新上线后继续消费

注意两点:

1.每个分区的数据只能由消费者组中一个消费者消费

2.一个消费者可以消费多个分区数据

消费者组

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

  1. 消费者组内每个消费者负责消费不同分区的数据,一个分区只能由一个组内消费者消费

  1. 消费者组之间互不影响。所有的消费者都属于某个消费者组,即消费者组是逻辑上的一个订阅者

  1. 如果向消费组中添加更多的消费者,超过主题分区数量,则有一部分消费者就会闲置,不会接收任何消息

消费者组初始化流程

消费者组的groupid是由用户在代码中手动创建的;

消费者组详细消费流程

相关参数:

Fetch.min.bytes:每批次最小抓取大小,默认1字节

fetch.max.wait.ms:一批数据最小值未达到的超时时间,默认500ms

Fetch.max.bytes:每批次最大抓取大小,默认50m

消费者重要参数

消费者 API

配置文件:

         Properties properties = new Properties();

        //消费者相关配置
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG,"hadoop102:9092,hadoop103: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,"test");
独立消费者
订阅主题(topic)

subscribe方法订阅主题

        //创建消费者对象
        Consumer<String, String> consumer = new KafkaConsumer<String, String>(properties);

        //注册要消费的主题(可以同时消费多个主题)
        ArrayList<String> topics = new ArrayList<>();
        topics.add("first");
        consumer.subscribe(topics);

        //消费数据
        while(true) //一直拉取
        {
            //设置1s消费一批数据
            ConsumerRecords<String, String> consumerRecords = consumer.poll(Duration.ofSeconds(1));

            //打印数据
            for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
                System.out.println(consumerRecord);
            }
        }
订阅分区(partition)

assign方法注册主题和分区

        Consumer<String, String> consumer = new KafkaConsumer<String, String>(properties);

        //注册要消费的主题和分区
        ArrayList<TopicPartition> topicPartition = new ArrayList<>();
        topicPartition.add(new TopicPartition("first",0));
        consumer.assign(topicPartition);


        while(true) //一直拉取
        {
            //拉取数据
            ConsumerRecords<String, String> consumerRecords = consumer.poll(Duration.ofSeconds(1));

            //打印数据
            for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
                System.out.println(consumerRecord);
            }
        }
消费者组

同时启动两份消费者代码

然后启动生产者代码进行数据发送(如果只发送到一个分区,可以在发送时增加延迟代码 Thread.sleep(2);)

可以看到,消费者组中不同的消费者消费不同分区的数据

生产经验

分区的分配以及再平衡

分区分配策略:

一个consumer group中有多个consumer组成,一个 topic有多个partition组成,现在的问题是,到底由哪个consumer来消费哪个partition的数据

Kafka有四种主流的分区分配策略: Range、RoundRobin、Sticky、CooperativeSticky

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

Range

RoundRobin

如何在代码中修改分区分配策略:

// 修改分区分配策略
properties.put(ConsumerConfig.PARTITION_ASSIGNMENT_STRATEGY_CONFIG, "org.apache.kafka.clients.consumer.RoundRobinAssignor");

Sticky

粘性分区:在执行一次新的分配之前,考虑上一次分配的结果,尽量少的调整分配的变动,可以节省大量的开销

粘性分区是 Kafka 从 0.11.x 版本开始引入这种分配策略,首先会尽量均衡的放置分区到消费者上面,在出现同一消费者组内消费者出现问题的时候,会尽量保持原有分配的分区不变化

举例:

1.初始消费者分配到的分区情况:

0 号消费者:消费到 0、1 号分区数据。

1 号消费者:消费到 2、5、3 号分区数据。

2 号消费者:消费到 4、6 号分区数据。

2.关闭0号消费者:

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

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

3.再次重新发送消息观看结果(45s 以后)

1 号消费者:消费到 2、3、5 号分区数据。

2 号消费者:消费到 0、1、4、6 号分区数据。

说明:消费者 0 已经被踢出消费者组,所以重新按照粘性方式分配

数据积压(消费者如何提高吞吐量)

1.如果是Kafka消费能力不足,则可以考虑增加Topic的分区数,并且同时提升消费组的消费者数量,消费者数 = 分区数。(两者缺一不可)

2.如果是下游的数据处理不及时:提高每批次拉取的数量。批次拉取数据过少(拉取数据/处理时间 < 生产速度),使处理的数据小于生产的数据,也会造成数据积压

相关参数:

fetch.max.bytes:消费者获取服务器端一批消息最大的字节数,默认是52428800(50 m);如果服务器端一批次的数据大于该值(50m)仍然可以拉取回来这批数据,因此,这不是一个绝对最大值。一批次的大小受 message.max.bytes (broker config)or max.message.bytes (topic config)影响

max.poll.records:一次 poll 拉取数据返回消息的最大条数,默认是 500 条

offset 位移

从0.9版本开始,consumer默认将offset保存在Kafka一个内置的topic中,该topic为__consumer_offsets

__consumer_offsets 主题里面采用 key 和 value 的方式存储数据。key 是 group.id+topic+分区号,value 就是当前 offset 的值。每隔一段时间,kafka 内部会对这个 topic 进行compact,也就是每个group.id+topic+分区号就保留最新数据

key的含义:记录某一个消费者组中的消费者消费某一个主题某一分区的数据的offset位置

查看系统主题数据

首先在配置文件 config/consumer.properties 中添加配置 exclude.internal.topics=false

默认是 true,表示不能消费系统主题。为了查看该系统主题数据,所以该参数修改为 false

指令:

bin/kafka-console-consumer.sh --topic __consumer_offsets --bootstrap-server hadoop102:9092 --consumer.config config/consumer.properties --formatter "kafka.coordinator.group.GroupMetadataManager\$OffsetsMessageFormatter" --from-beginning

自动提交offset

为了使我们能够专注于自己的业务逻辑,Kafka提供了自动向__consumer_offsets提交offset的功能

相关参数:

enable.auto.commit:是否开启自动提交offset功能,默认是true

auto.commit.interval.ms:自动提交offset的时间间隔,默认是5s

代码中进行设置:

// 是否自动提交 offset
properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true);
// 提交 offset 的时间周期 1000ms,默认 5s
properties.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, 1000);
手动提交 offset

虽然自动提交offset十分简单便利,但由于其是基于时间提交的,开发人员难以把握offset提交的时机。因此Kafka还提供了手动提交offset的API

方法:commitSync(同步提交)和commitAsync(异步提交)

相同点:都会将本次提交的一批数据最高的偏移量提交

不同点:同步提交阻塞当前线程,一直到提交成功,并且会自动失败重试(由不可控因素导致,也会出现提交失败);而异步提交则没有失败重试机制,故有可能提交失败。

commitSync(同步提交):必须等待offset提交完毕,再去消费下一批数据。

commitAsync(异步提交) :发送完提交offset请求后,就开始消费下一批数据了

相关代码:

// 设置自动提交为false
 properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false);

消费数据之后通过consumer.commitSync()方法同步提交offset

//消费数据
 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();
 }

异步提交和同步提交相同,只是把方法换成了consumer.commitAsync();

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

THE WHY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值