Java KafkaConsumer.commitAsync()行为,偏移量低于上一个

Java KafkaConsumer.commitAsync()行为,偏移量低于上一个,java,apache-kafka,kafka-consumer-api,offset,spring-kafka,Java,Apache Kafka,Kafka Consumer Api,Offset,Spring Kafka,卡夫卡将如何处理一个电话 KafkaConsumer.commitAsync(映射偏移量,OffsetCommitCallback回调) 当一个主题的偏移量值小于上一次调用时?它只需将分区的偏移量设置为您指定的值,因此下次您将使用CommittedOffset+1中的消息。 commitAsync()的javadoc说明: 提交的偏移量应该是应用程序将使用的下一条消息,即lastProcessedMessageOffset+1 我很好奇,并测试了它的行为。正如文件中所写的那样,@haoyuwan

卡夫卡将如何处理一个电话

KafkaConsumer.commitAsync(映射偏移量,OffsetCommitCallback回调)


当一个主题的偏移量值小于上一次调用时?

它只需将分区的偏移量设置为您指定的值,因此下次您将使用CommittedOffset+1中的消息。
commitAsync()
的javadoc说明:

提交的偏移量应该是应用程序将使用的下一条消息,即lastProcessedMessageOffset+1


我很好奇,并测试了它的行为。正如文件中所写的那样,@haoyuwang在他的回答中所写的是正确的(+1)

原因很简单。消费者组的提交偏移量存储在Kafka的内部主题
\uuu consumer\u offset
中。本主题是
紧凑型
,这意味着它要为给定的键提供最新的值。在您的例子中,键是消费者组、主题和分区的组合,而您的值是偏移量

如果你现在

  • 提交偏移量10,由于稍后的异步进程
  • 提交偏移量5
偏移量5将是
消费者偏移量
主题中的最新值。这意味着您的消费者将从该主题分区读取的下一个偏移量是偏移量6,而不是偏移量11

如何繁殖 您只需在常规提交之后(同步地)提交一个较早的偏移量,即可复制并测试它,如下所示:

consumer.commitSync();
consumer.commitSync(commitFirstMessage);
其中
commitFirstMessage
定义为

TopicPartition zeroTopicPartition=新的TopicPartition(主题,0);
OffsetAndMetadata zeroOffset=新的OffsetAndMetadata(0L);
Map commitFirstMessage=newhashmap();
commitFirstMessage.put(zeroTopicPartition,zeroOffset);

编辑:

如何避免使用commitAsync提交较低的偏移量 书中建议避免提交较低的偏移量,因为重试调用
commitAsync

重试异步提交:为异步重试获得正确的提交顺序的一个简单模式是使用单调递增的序列号。每次提交并添加 提交到commitAsync回调时的序列号。准备发送重试时,请检查回调得到的提交序列号是否等于实例变量;如果是,则没有新的提交,可以安全地重试。如果实例序列号较高,请不要重试,因为已发送较新的提交

一个实现可能是这样的(实际上没有经过测试!):

import java.util_
导入java.time.Duration
导入org.apache.kafka.clients.consumer.{ConsumerConfig,ConsumerRecord,KafkaConsumer,OffsetAndMetadata,OffsetCommitCallback}
导入org.apache.kafka.common.{KafkaException,TopicPartition}
导入collection.JavaConverters_
对象AsyncCommitWithCallback扩展应用程序{
//定义主题
val topic=“myOutputTopic”
//设置属性
val props=新属性()
props.put(ConsumerConfig.GROUP\u ID\u CONFIG,“AsyncCommitter5”)
put(ConsumerConfig.BOOTSTRAP\u SERVERS\u CONFIG,“localhost:9092”)
//[设置更多属性…]
//创建KafkaConsumer并订阅
val consumer=new KafkaConsumer[String,String](道具)
consumer.subscribe(列表(主题).asJavaCollection)
//初始化全局计数器
val atomicLong=新的atomicLong(0)
//消费信息
试一试{
while(true){
val记录=消费者投票(百万持续时间(1)).asScala
if(记录非空){
用于(数据例如printStackTrace()
}最后{
consumer.commitSync()
consumer.close()
}
类KeepOrderAsyncCommit扩展了OffsetCommitCallback{
//保持此回调实例的位置
val position=atomicLong.incrementAndGet()
覆盖def onComplete(偏移量:util.Map[TopicPartition,OffsetAndMetadata],异常:异常):单位={
//仅当没有其他提交增加全局计数器时重试
if(异常!=null){
if(position==atomicLong.get){
consumer.commitAsync(此)
}
}
}
}
}