KAFKA消费者-进阶用法

Apache Kafka 是一个分布式流处理平台,用于构建实时流数据管道和应用程序。在 Kafka 中,消费者(Consumer)用于从 Kafka 主题(Topic)中读取消息并进行处理。本文将介绍 Kafka 消费者的进阶用法,包括手动提交偏移量、消费者组、重新平衡等功能。

一. 创建 Kafka 消费者

首先,我们需要创建 Kafka 消费者,并配置消费者属性。

而创建消费在方式有俩种,一种是注解,另一种则是通过new KafkaConsumer()的方式来获取到消费者实例;

1.注解:

@KafkaListener(topic = "your-topic")
    public void handle(ConsumerRecord consumerRecord) {
        System.out.println("消费者消费消息:" + consumerRecord);
        System.out.println(String.format("消费者收到消息,topic:%s,partition:%s", consumerRecord.topic(), consumerRecord.partition()));
        System.out.println("消费内容:" + consumerRecord.value());
    }

@KafkaListener(topics = {"your-topic1", "your-topic2"})
    public void handleCMDB(ConsumerRecord consumerRecord) {
        System.out.println("消费者消费消息:" + consumerRecord);
        System.out.println(String.format("消费者收到消息,topic:%s,partition:%s", consumerRecord.topic(), consumerRecord.partition()));
        logger.info(String.format("消费者收到消息,topic:%s,partition:%s", consumerRecord.topic(), consumerRecord.partition()));
        System.out.println("消费内容:" + consumerRecord.value());
    }

 使用注解通过topic=指定需要监听的通道,同时可以使用topics监听多个通道,通过consumerRecord.value()即可获取到通道中的数据值;而kafka的配置需要在yml配置文件中指定

2.new KafkaConsumer()实例:

使用创建实例的方式需要指定properties配置,否则获取到的消费者实例为空,没有任何意义,而创建消费者来进行消费的方法有俩种,一种方式必须指定groupid,一种方式是默认会指定groupid不需要手动指定,以下是代码示例:

// 方式1:需要指定gruopid
public String useMsg(){
        Properties props = new Properties();
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "yourKafkaServers");
        props.put(ConsumerConfig.GROUP_ID_CONFIG, "defaultConsumerGroup"); // 默认组
        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());

        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        consumer.subscribe(Collections.singletonList("kafka_test_topic"));

        try {
            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());
                }
            }
        } catch (WakeupException e) {
            // Ignore exception for shutdown
        } finally {
            consumer.close();
        }
        return "消费成功";
    }

// 方式2:无需手动指定gruopid
public String useOtherMsg(){
        Properties props = new Properties();
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "yourKafkaServers");
        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());

        KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
        // 使用TopicPartition指定消费者主题和分区
        TopicPartition topicPartition = new TopicPartition("yourtopic", 0);
        consumer.assign(Arrays.asList(topicPartition));

        try {
            while (true) {
                ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
                for (ConsumerRecord<String, String> record : records) {
                    System.out.println("消费消息!!!!");
                    System.out.printf("offset = %d, key = %s, value = %s%n", record.offset(), record.key(), record.value());
                }
            }
        } catch (WakeupException e) {
            // Ignore exception for shutdown
        } finally {
            consumer.close();
        }
        return "无group消费者消费数据";
    }

二. 手动提交偏移量

手动提交偏移量可以确保消息被成功处理后再提交偏移量,避免消息丢失或重复消费。

设置偏移量有一种简单粗暴的方式:

consumer.seek(topicPartition, 0);

直接将偏移量设置为0,即从队列最开始的地方读取数据

第二种方式根据时间戳来设置偏移量,代码如下:

TopicPartition topicPartition = new TopicPartition("yourtopic", 0);
            consumer.assign(Arrays.asList(topicPartition));
            // 指定偏移量开始时间戳,默认为当天00:00的时间戳
            long timestampToSearch = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toEpochSecond() * 1000;
            if (kafkaConfig.getLastRunTime() != null) {
                // 默认十秒提前量容错
                long advanceTime = kafkaConfig.getAdvanceTime() != null ? kafkaConfig.getAdvanceTime() : 10000;
                timestampToSearch = kafkaConfig.getLastRunTime().getTime() - advanceTime;
            }
            logger.info("设置当前偏移量开始时间戳:" + timestampToSearch);
            // 查找每个分区在指定时间的偏移量
            Map<TopicPartition, Long> timestampsToSearch = new HashMap<>();
            timestampsToSearch.put(topicPartition, timestampToSearch);
            Map<TopicPartition, OffsetAndTimestamp> offsetsForTimes = consumer.offsetsForTimes(timestampsToSearch);
            // 将消费者指针移动到指定时间的偏移量
            for (Map.Entry<TopicPartition, OffsetAndTimestamp> entry : offsetsForTimes.entrySet()) {
                TopicPartition partition = entry.getKey();
                OffsetAndTimestamp offsetAndTimestamp = entry.getValue();
                if (offsetAndTimestamp != null) {
                    consumer.seek(partition, offsetAndTimestamp.offset());
                }
            }

这里的getLastRunTime即为获取到上次从队列中退出时的时间,advanceTime为容错时间,即退出时间需要往前推迟多久,这样可能会读取到重复的一俩条数据,但并不会丢失数据,有利有弊且利大于弊;有细心读者会发现这里的队列设置偏移量是使用的不需要指定groupid的消费者,那么指定groupid的消费者该如何设置偏移量呢?别急,下面就给你们端上来:
 

consumer.subscribe(Collections.singletonList("yourtopic"), new ConsumerRebalanceListener() {
                @Override
                public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
                    // 分区被撤销时的处理,这里可以清空任何与这些分区相关的信息
                }

                @Override
                public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
                    // 分区被分配时的处理
                    TopicPartition topicPartition = new TopicPartition("yourtopic", 0);
                    // 指定偏移量开始时间戳,默认为当天00:00的时间戳
                    long timestampToSearch = LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toEpochSecond() * 1000;
                    if (kafkaConfig.getLastRunTime() != null) {
                        // 默认十秒提前量容错
                        long advanceTime = kafkaConfig.getAdvanceTime() != null ? kafkaConfig.getAdvanceTime() : 10000;
                        timestampToSearch = kafkaConfig.getLastRunTime().getTime() - advanceTime;
                    }
                    logger.info("设置当前偏移量开始时间戳:" + timestampToSearch);
                    Map<TopicPartition, Long> timestampsToSearch = Collections.singletonMap(topicPartition, timestampToSearch);
                    Map<TopicPartition, OffsetAndTimestamp> offsetsForTimes = consumer.offsetsForTimes(timestampsToSearch);
                    for (Map.Entry<TopicPartition, OffsetAndTimestamp> entry : offsetsForTimes.entrySet()) {
                        OffsetAndTimestamp offsetAndTimestamp = entry.getValue();
                        if (offsetAndTimestamp != null) {
                            consumer.seek(entry.getKey(), offsetAndTimestamp.offset());
                        }
                    }
                }
            });

通过内部类,重写了ConsumerRebalanceListener 接口的实现来处理分区的重新分配情况,同时根据时间戳重新设置偏移量,这也是所谓的重新平衡,以实现从指定时间点开始消费消息的功能

三. 消费者组和重新平衡

消费者组允许多个消费者共同消费一个主题,并通过重新平衡(Rebalance)来分配分区给不同的消费者。

props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "false");
props.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 1);

consumer.subscribe(Collections.singletonList(TOPIC_NAME));

consumer.poll(0); // 触发消费者加入消费者组
consumer.seekToBeginning(consumer.assignment());

consumeMessages(consumer);

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
kafka是一个分布式的流处理平台,它可以处理和存储大规模的实时数据流。kafka-run-class是kafka提供的一个脚本工具,用于在命令行中执行指定的类。kafka.tools.GetOffsetShell是用于获取指定topic在指定时间点的偏移量信息的工具。 具体来说,kafka.tools.GetOffsetShell通过指定topic、时间点和broker列表来获取该topic在指定时间点的偏移量。它的用法类似于以下命令: ``` bin/kafka-run-class.sh kafka.tools.GetOffsetShell --topic <topic> --time <timestamp> --broker-list <broker-list> --partitions <partitions> ``` 其中,<topic>是要查询的topic名称,<timestamp>是要查询的时间点,<broker-list>是broker的地址列表,<partitions>是要查询的分区编号。该命令会返回指定topic在指定时间点的偏移量信息。 另外,kafka.tools.ConsumerOffsetChecker是kafka提供的另一个工具,用于检查消费者的偏移量信息。它可以通过指定zookeeper连接、消费者组和topic来获取消费者的偏移量信息。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [kafka监控命令kafka-run-class.sh查看消费了多少条数据](https://blog.csdn.net/m0_37739193/article/details/73849523)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [kafka_2.9.2-0.8.2.1.tgz](https://download.csdn.net/download/shy_snow/9372309)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值