kafka(八:位移提交offset)

 老版本的kafka Offset是Consumer 的消费位移,记录了 Consumer 要消费的下一条消息的位移。

Consumer 需要向 Kafka 汇报自己的位移数据,这个过程被称为提交位移。 Consumer 能够同时消费多个分区的数据,所以位移的提交实际上是在分区粒度上进行的,即Consumer 需要为分配给它的每个分区提交各自的位移数据。

提交位移主要是为了表征 Consumer 的消费进度,这样当 Consumer 发生故障重启之后,就能够从 Kafka 中读取之前提交的位移值,然后从相应的位移处继续消费,从而避免整个消费过程重来一遍。

 

provider角度来说位移提交分为自动提交和手动提交; Consumer 端的角度来说,位移提交分为同步提交和异步提交。

Consumer 端 KafkaConsumer.commitSync()会提交 KafkaConsumer.poll() 返回的最新位移。它是一个同步操作,即该方法会一直等待,直到位移被成功提交才会返回。如果提交过程中出现异常,该方法会将异常信息抛出。

调用 consumer.commitSync() 方法的时机是在处理完了 poll() 方法返回的所有消息之后。如果过早提交了位移,就可能会出现消费数据丢失的情况。

一旦设置了 enable.auto.commit 为 true,Kafka 会保证在开始调用 poll 方法时,提交上次 poll 返回的所有消息。从顺序上来说,poll 方法的逻辑是先提交上一批消息的位移,再处理下一批消息,因此它能保证不出现消费丢失的情况。但自动提交位移的一个问题在于,它可能会出现重复消费

在默认情况下,Consumer 每 5 秒自动提交一次位移。假设提交位移之后的 3 秒发生了 Rebalance 操作。在 Rebalance 之后,所有 Consumer 从上一次提交的位移处继续消费,但该位移已经是 3 秒前的位移数据了,故在 Rebalance 发生前 3 秒消费的所有数据都要重新再消费一次。虽然能够通过减少 auto.commit.interval.ms 的值来提高提交频率,但这么做只能缩小重复消费的时间窗口,不可能完全消除它。这是自动提交机制的一个缺陷。

手动提交位移好处就在于更加灵活,完全能够把控位移提交的时机和频率。但是在调用 commitSync() 时,Consumer 程序会处于阻塞状态,直到远端的 Broker 返回提交结果,这个状态才会结束。如果因为网络问题broker端一直迟迟不返回信息,这将会极大影响整个应用的TPS。于是kafka提供了另一个异步的方法 KafkaConsumer.commitAsync()。用 commitAsync() 之后,它会立即返回,不会阻塞,因此不会影响 Consumer 应用的 TPS。commitAsync提供了回调函数(callback)提交之后的逻辑,比如记录日志或处理异常等。但是commitAsync 出现问题时它不会自动重试。它是异步操作,如果提交失败后自动重试,那么它重试时提交的位移值可能早已经“过期”或不是最新值了。

手动提交可以将 commitSync 和 commitAsync 组合使用。如下调用 commitAsync() 避免程序阻塞,而在 Consumer 要关闭前调用 commitSync() 方法执行同步阻塞式的位移提交,以确保 Consumer 关闭前能够保存正确的位移数据。将两者结合后,我们既实现了异步无阻塞式的位移管理,也确保了 Consumer 位移的正确性。

   try {
            while (true) {
                        ConsumerRecords<String, String> records = 
                                    consumer.poll(Duration.ofSeconds(1));
                        process(records); // 处理消息
                        commitAysnc(); // 使用异步提交规避阻塞
            }
} catch (Exception e) {
            handle(e); // 处理异常
} finally {
            try {
                        consumer.commitSync(); // 最后一次提交使用同步阻塞式提交
	} finally {
	     consumer.close();
}
}

当一次性要处理大批量的消息时,肯定是不希望等消息完全消费完后再提交位移,如果中途发生点网络抖动或者Bug导致前功尽弃。于是可以一次消费一部分就提交。

Kafka Consumer API 提交提供了这样的方法:commitSync(Map<TopicPartition, OffsetAndMetadata>) 和 commitAsync(Map<TopicPartition, OffsetAndMetadata>)。它们的参数是一个 Map 对象,键就是 TopicPartition,值是一个 OffsetAndMetadata 对象,保存的主要是位移数据。

private Map<TopicPartition, OffsetAndMetadata> offsets = new HashMap<>();
int count = 0;
……
while (true) {
            ConsumerRecords<String, String> records = 
	consumer.poll(Duration.ofSeconds(1));
            for (ConsumerRecord<String, String> record: records) {
                        process(record);  // 处理消息
                        offsets.put(new TopicPartition(record.topic(), record.partition()),
                                    new OffsetAndMetadata(record.offset() + 1);
                        if(count % 100 == 0)
                                    consumer.commitAsync(offsets, null); // 回调处理逻辑是 null
                        count++;
	}
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值