kafka consumer类中关于offset使用例子

本文详细介绍了Kafka消费者如何管理偏移量,包括自动和手动提交偏移量,人工分区分配,以及在外部存储偏移量。重点讨论了消费者位置的概念,如位置(position)和提交位置(commit position),并提供了使用示例,帮助理解如何控制消费者的消费流程和流量控制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

kafka consumer类中关于offset使用例子

偏移量和消费者位置

kafka为分区中的每一个记录维护一个数字偏移量。偏移量是分区中消费者独一无二特征,标记消费者在分区中的位置,例如一个消费者在位置5则表示已经消费了0到4位置的记录,而且将收到位置5的记录,这里有两个关于消费者位置的概念:
位置(position)代表下一个将要得到的记录的偏移量。它的值比消费者在这个分区中已经见过的最大偏移量还要大,每次调用poll收到记录之后它会自动前进。
提交位置(commit position)是已经被安全存储的最后偏移量,程序失败或者重启的时候它是程序恢复的偏移量,消费者可以选择周期性自动提交偏移量,或者通过调用提交API(commitSync或commitAsync)人为控制偏移量的提交.
这种区别可以让消费者控制何时认为记录已经被消费了。

1. 自动提交偏移量
 Properties props = new Properties();   
 props.setProperty("bootstrap.servers", "localhost:9092");
 props.setProperty("group.id", "test");
 props.setProperty("enable.auto.commit", "true");
 props.setProperty("auto.commit.interval.ms", "1000");
 props.setProperty("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
 props.setProperty("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
 KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
 consumer.subscribe(Arrays.asList("foo", "bar"));
 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());
 }

通过使用配置bootstrap.servers来制定一个或者多个代理的列表建立到集群的连接。不必写上集群全部节点,通过一个可以发现其他全部节点。
设置enable.auto.commit以固定的auto.commit.interval.ms频率来提交偏移量。
本例中,使用者订阅主题far和bar,作为test消费者组的一部分,由group.id配置。

2. 人为偏移量控制

用户可以控制记录应该被认为已经消费时,提交偏移量。这样在消息消费和某些处理逻辑相关的时候,一条消息直到相关进程完成的时候才被认为已被消费。

Properties props = new Properties();
 props.setProperty("bootstrap.servers", "localhost:9092");
 props.setProperty("group.id", "test");
 props.setProperty("enable.auto.commit", "false");
 props.setProperty("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
 props.setProperty("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
 KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
 consumer.subscribe(Arrays.asList("foo", "bar"));
 final int minBatchSize = 200;
 List<ConsumerRecord<String, String>> buffer = new ArrayList<>();
 while (true) {
     ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(100));
     for (ConsumerRecord<String, String> record : records) {
         buffer.add(record);
     }
     if (buffer.size() >= minBatchSize) {
         insertIntoDb(buffer);
         consumer.commitSync();
         buffer.clear();
     }
 }

本例中我们将批量消费记录并把他们缓存在内存中。当有足量的记录,我们将他们存入数据库。如果按照前一种方式,批量缓存和插入数据库之间我们的进程可能会失败。为避免这种情况,在相应记录存入数据库之后将人工提交偏移量。这让我们对记录消费有精确控制。有以下好处:进程可以在插入数据库未提交的间隙失败,这种情况下进程会重新执行上一次的偏移量,重复插入数据库一次。kafka用这种方式提供一种至少一次的传输保证。
上述例子利用commitSync()标记所有收到的记录已被消费。有时候你想更好的控制,通过明确指定偏移量记录已被消费情况,下边的例子我们提交偏移量,在处理完每一个分区记录之后。

try {
     while(running) {
         ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(Long.MAX_VALUE));
         for (TopicPartition partition : records.partitions()) {
             List<ConsumerRecord<String, String>> partitionRecords = records.records(partition);
             for (ConsumerRecord<String, String> record : partitionRecords) {
                 System.out.println(record.offset() + ": " + record.value());
             }
             long lastOffset = partitionRecords.get(partitionRecords.size() - 1).offset();
             consumer.commitSync(Collections.singletonMap(partition, new OffsetAndMetadata(lastOffset + 1)));
         }
     }
 } finally {
   consumer.close();
 }
 注意:提交的偏移量应该是下一次开始读的地方,因此,调用 commitSync(offsets) 时你应该添加最后一条执行消息的偏移量之一。
人为分区分配

前边例子中,我们订阅主题,然后kafka依据组中的活跃用户为这些主题动态分配分区的公平份额。有时候你可能需要对特殊的被分配的分区更好的控制。例如:

  • 如果进程包含一些和某个分区关联的本地状态(比如本地磁盘上的键值存储),因此应该仅仅获取该分区包含的在磁盘上的记录

  • 如果进程本身是高可用的,而且会失败自动重启(可能使用集群管理框架比如YARN、Mesos、AWS,或者一个流进程框架的一部分)。这种情况下,对于kafka来说没必要去检测宕机,重新分配分区,因为消费进程将会在其他机器上重启。

    这种模式下,不通过subscribe订阅主题,你调用assign(Collection)使用你想消费的所有分区的列表。

    String topic = “foo”;
    TopicPartition partition0 = new TopicPartition(topic, 0);
    TopicPartition partition1 = new TopicPartition(topic, 1);
    consumer.assign(Arrays.asList(partition0, partition1));
    一旦分配好,你可以调用poll在循环中,和之前的例子一样。消费者指定的组仍然被用来提交偏移量,但是现在这个分区集合将随着仅另一个调用assign而改变。人工分区分配不使用组协调,因此消费者宕机将不会造成已分配分区的重新平衡。每一个消费者行为独立即使和其它消费者共享groupID。为了避免偏移量提交冲突,你应该经常检查以确保每一个消费者的groupID是独一无二的。
    注意,通过主题订阅(比如subscribe)来动态分区分配以最大化分区分配(比如使用assign)是不可能的。

在kafka外保存偏移量

消费者不用使用kafka内置的偏移量存储,可以使用自己选择的存储。这种情况的基本使用场景是 允许应用程序存储消费的偏移量和结果到同一个系统中,通过结果和偏移量自动存储的方式。这并不总是可能的,但是当可以使消费完全自动化时,确保“只有一次”时,比默认的kafka偏移量提交功能“至少一次”更强大。
这种类型的两个例子:

  • 如果消费结果存储在关系型数据库,偏移量存储的数据库可以允许提交结果和偏移量在同一个事件中。因此要么事件执行成功,偏移量会依据消费更新,要么结果不会存储,偏移量不会更新。
  • 如果结果存储在本地存储,偏移量也可能存在这里。比如通过订阅特定分区和一起存储偏移量和索引数据来构建索引数据。如果这通过自动方式进行,可能会发生这种情况,即使发生崩溃造成不同步数据丢失,剩下的数据有对应的偏移量被保存下来。这意味着这种情况下,返回最近更新失败的索引进程只确保从它确定没有更新丢失的内容中重新开始索引。
    每一个记录伴随着他自己的偏移量,所以管理自己的偏移量需要如下做:
    • 设置 enable.auto.commit=false
    • 使用每一个ConsumerRecord 提供的偏移量保存你的位置
    • 重新启动时使用seek恢复用户位置

当分区分配也是人工时(在搜索索引情况下和上述描述类似),这种使用是最简单的。如果分区分配是自动执行的,当分区分配改变时,需要更多地精力来处理。这可以通过提供一个 ConsumerRebalanceListener实例来调用 subscribe(Collection, ConsumerRebalanceListener) subscribe(Pattern, ConsumerRebalanceListener),例如,当从消费者中拿走的分区,这个消费者想通过实现ConsumerRebalanceListener.onPartitionsRevoked(Collection)来提交这些分区的偏移量。当分区分配给一个消费者,这个消费者将会想找到这些这些新分区的偏移量,准确初始化消费者到那个位置通过实现ConsumerRebalanceListener.onPartitionsAssigned(Collection).
另一个ConsumerRebalanceListener 常见使用是清空应用程序包含的移动到别处的分区的缓存。

控制消费者位置

大多数场景下,消费者将简单地从头到尾消费记录,周期性提交它的位置(自动或人为的)。然而,kafka允许消费者人为控制它的位置,按照意愿在一个位置上往前或者往后。这意味着一个消费者可以重复消费旧的记录,或者跳到最近的记录而不使用中间的记录。
这里有几个人为控制消费者位置很有用的例子。
一种情况是时间敏感型记录程序会有效,一个消费者落后太多以至于没有尝试执行所有的记录,但是直接跳到最近的位置。
另一个场景是包含本地状态的如前所述的系统。这样系统中,消费者可能启动初始化它的位置到包含本地存储的位置。同样地,如果本地状态被毁坏(比如磁盘丢失),状态可能在一台新的机器上创建,然后重新消费所有的数据、重建(假设kafka保留了足够的历史)。
kafka允许通过seek(TopicPartition, long)指定一个新的特指位置。服务业也包含找到最开始和最近未知的特殊方法seekToBeginning(Collection) and seekToEnd(Collection) 。

消费流量控制

如果一个消费者分配了多个分区拿取数据,它将尝试同时从它们消费数据,有效地赋予这些分区同样的消费优先级。然而有时候消费者可能想专注于全速从分配分区中的一部分分区拿取数据,当这些分区的数据将近使用完毕或者已经使用完毕时才开始其他的分配分区。
一种情况是流进程,处理器从两个主题拿取数据然后对两个流进行拼接。当一个主题远远落后于另外一个,处理器可能会挂起从领先主题拿取数据,以便落后的流跟上来。另一个例子是启动时候,消费者有大量的历史数据要跟上来,应用经常会想在这些主题上拿到最近的数据,在考虑从其他主题拿取数据之前。
kafka支持动态控制消费流量,通过 pause(Collection)来挂起指定分配分区的消费, 和resume(Collection) 在以后的poll(Duration)轮询中重新开始指定的挂起分区。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值