Apache kafka Kafka consumer在消息处理失败后恢复

Apache kafka Kafka consumer在消息处理失败后恢复,apache-kafka,kafka-consumer-api,Apache Kafka,Kafka Consumer Api,我正在我的一个项目中使用简单的kafka消费者,我想要的逻辑是,当消费者处理某些消息失败时,它将提交上次正确处理的消息,然后在下一次轮询时,它将从失败的消息继续 我尝试使用以下代码手动提交每条消息: public void fetchMessages() { ConsumerRecords<String, MyObject> messages = kafkaConsumer.poll(10000); for (ConsumerRecord message : messages

我正在我的一个项目中使用简单的kafka消费者,我想要的逻辑是,当消费者处理某些消息失败时,它将提交上次正确处理的消息,然后在下一次轮询时,它将从失败的消息继续

我尝试使用以下代码手动提交每条消息:

public void fetchMessages() {
  ConsumerRecords<String, MyObject> messages = kafkaConsumer.poll(10000);
  for (ConsumerRecord message : messages) {
      logger.info("Reading kafka message, topic ["+kafkaTopic+"], partition ["+message.partition()+"], offset ["+message.offset()+"]");
      try {
          MyObject myObject = (MyObject) message.value();
          logger.info("Handling message," + myObject);
          handleMessage(myObject);
          commitMessage(message);
      } catch (Exception e) {
          logger.error("Error handling message");              throw e;
      }
  }
}


private void commitMessage(ConsumerRecord message) {
        long              nextOffset        = message.offset() + 1;

        TopicPartition    topicPartition    = new TopicPartition(kafkaTopic,message.partition());
        OffsetAndMetadata offsetAndMetadata = new OffsetAndMetadata(nextOffset);

        Map<TopicPartition,OffsetAndMetadata> offsetAndMetadataMap = new HashMap<>();
        offsetAndMetadataMap.put(topicPartition,offsetAndMetadata);

        logger.info("Commiting processed kafka message, topic ["+kafkaTopic+"], partition ["+message.partition()+"], next offset ["+nextOffset+"]");
        kafkaConsumer.commitSync(offsetAndMetadataMap);
}
一些细节:

  • 主题有12个分区
  • 所有分区的一个使用者
  • 消费者每分钟执行一次轮询循环
  • enable.auto.commit:false

我的代码或逻辑有什么问题?

我找到了seek的工作原理,在失败的消息中,我必须查找当前使用者的所有分区的所有偏移量

private void seekAllPartitions() {
    logger.info("Processing of some kafka message was failed, seeking all partitions to last committed");
    List<PartitionInfo> partitionInfos = kafkaConsumer.partitionsFor(kafkaTopic);
    for (PartitionInfo partitionInfo : partitionInfos) {
        TopicPartition topicPartition = new TopicPartition(kafkaTopic, partitionInfo.partition());
        OffsetAndMetadata committedForPartition = kafkaConsumer.committed(topicPartition);
        if (committedForPartition != null) {
            kafkaConsumer.seek(topicPartition,committedForPartition.offset());
        }
    }
}
private void seekAllPartitions(){
info(“处理某些kafka消息失败,正在查找所有上次提交的分区”);
List partitioninfo=kafkaConsumer.partitionsFor(kafkaTopic);
for(PartitionInfo PartitionInfo:PartitionInfo){
TopicPartition TopicPartition=新的TopicPartition(kafkaTopic,partitionInfo.partition());
OffsetAndMetadata CommittedOrpartition=kafkaConsumer.committed(主题分区);
if(committedForPartition!=null){
kafkaConsumer.seek(topicPartition,committeedorpartition.offset());
}
}
}

当某个分区上某个用户组的最后一个偏移量尚未设置(未知)时,需要对committedForPartition进行空检查。

我相信您不想将“1”添加到正在提交的偏移量中。相反,您希望提交正确使用的偏移量。这就解释了在你的例子中有一条失败的消息没有被重放…@jimijazz我认为你是不对的。请看一下Kafka消费者API-commitSync方法。提交的偏移量应该是应用程序将使用的下一条消息,即lastProcessedMessageOffset+1。我想你是对的@mixermt,谢谢你指出这一点。。。但我不明白它的逻辑。对
poll()
的后续调用是否会错过每个批中的第一个偏移量,因为它已被上一次迭代标记为提交?
private void seekAllPartitions() {
    logger.info("Processing of some kafka message was failed, seeking all partitions to last committed");
    List<PartitionInfo> partitionInfos = kafkaConsumer.partitionsFor(kafkaTopic);
    for (PartitionInfo partitionInfo : partitionInfos) {
        TopicPartition topicPartition = new TopicPartition(kafkaTopic, partitionInfo.partition());
        OffsetAndMetadata committedForPartition = kafkaConsumer.committed(topicPartition);
        if (committedForPartition != null) {
            kafkaConsumer.seek(topicPartition,committedForPartition.offset());
        }
    }
}