springboot中实现kafka指定offset消费

1115df6a0266f75ada0e2c211719310a.png

kafka消费过程难免会遇到需要重新消费的场景,例如我们消费到kafka数据之后需要进行存库操作,若某一时刻数据库down了,导致kafka消费的数据无法入库,为了弥补数据库down期间的数据损失,有一种做法我们可以指定kafka消费者的offset到之前某一时间的数值,然后重新进行消费。
首先创建kafka消费服务

@Service
@Slf4j
//实现CommandLineRunner接口,在springboot启动时自动运行其run方法。
public class TspLogbookAnalysisService implements CommandLineRunner {
    @Override
    public void run(String... args) {
        //do something
    }
}

kafka消费模型建立
kafka server中每个主题存在多个分区(partition),每个分区自己维护一个偏移量(offset),我们的目标是实现kafka consumer指定offset消费。

在这里使用consumer-->partition一对一的消费模型,每个consumer各自管理自己的partition。
format,png

@Service
@Slf4j
public class TspLogbookAnalysisService implements CommandLineRunner {
    //声明kafka分区数相等的消费线程数,一个分区对应一个消费线程
    private  static final int consumeThreadNum = 9;
    //特殊指定每个分区开始消费的offset
    private List<Long> partitionOffsets = Lists.newArrayList(1111,1112,1113,1114,1115,1116,1117,1118,1119);
   
    private ExecutorService executorService = Executors.newFixedThreadPool(consumeThreadNum);
 
    @Override
    public void run(String... args) {
        //循环遍历创建消费线程
        IntStream.range(0, consumeThreadNum)
                .forEach(partitionIndex -> executorService.submit(() -> startConsume(partitionIndex)));
    }
}

 kafka consumer对offset的处理

声明kafka consumer的配置类

private Properties buildKafkaConfig() {
    Properties kafkaConfiguration = new Properties();
    kafkaConfiguration.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "");
    kafkaConfiguration.put(ConsumerConfig.GROUP_ID_CONFIG, "");
    kafkaConfiguration.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, "");
    kafkaConfiguration.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "");
    kafkaConfiguration.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "");
    kafkaConfiguration.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "");
    kafkaConfiguration.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"");
    kafkaConfiguration.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "");
    ...更多配置项
 
    return kafkaConfiguration;
}

创建kafka consumer,处理offset,开始消费数据任务

private void startConsume(int partitionIndex) {
    //创建kafka consumer
    KafkaConsumer<String, byte[]> consumer = new KafkaConsumer<>(buildKafkaConfig());
 
    try {
        //指定该consumer对应的消费分区
        TopicPartition partition = new TopicPartition(kafkaProperties.getKafkaTopic(), partitionIndex);
        consumer.assign(Lists.newArrayList(partition));
 
        //consumer的offset处理
        if (collectionUtils.isNotEmpty(partitionOffsets)  &&  partitionOffsets.size() == consumeThreadNum) {
            Long seekOffset = partitionOffsets.get(partitionIndex);
            log.info("partition:{} , offset seek from {}", partition, seekOffset);
            consumer.seek(partition, seekOffset);
        }
        
        //开始消费数据任务
        kafkaRecordConsume(consumer, partition);
    } catch (Exception e) {
        log.error("kafka consume error:{}", ExceptionUtils.getFullStackTrace(e));
    } finally {
        try {
            consumer.commitSync();
        } finally {
            consumer.close();
        }
    }
}

消费数据逻辑,offset操作

private void kafkaRecordConsume(KafkaConsumer<String, byte[]> consumer, TopicPartition partition) {
    while (true) {
        try {
            ConsumerRecords<String, byte[]> records = consumer.poll(TspLogbookConstants.POLL_TIMEOUT);
            //具体的处理流程
            records.forEach((k) -> handleKafkaInput(k.key(), k.value()));
 
            //🌿很重要:日志记录当前consumer的offset,partition相关信息(之后如需重新指定offset消费就从这里的日志中获取offset,partition信息)
            if (records.count() > 0) {
                String currentOffset = String.valueOf(consumer.position(partition));
                log.info("current records size is:{}, partition is: {}, offset is:{}", records.count(), consumer.assignment(), currentOffset);
            }
    
            //offset提交        
            consumer.commitAsync();
        } catch (Exception e) {
            log.error("handlerKafkaInput error{}", ExceptionUtils.getFullStackTrace(e));
        }
    }
}

相关介绍如下:

 

一、auto.offset.reset值详解

在 Kafka 中,每当消费者组内的消费者查找不到所记录的消费位移或发生位移越界时,就会根据消费者客户端参数 auto.offset.reset 的配置来决定从何处开始进行消费,这个参数的默认值为 “latest” 。

auto.offset.reset 的值可以为 earliest、latest 和 none 。关于 earliest 和 latest 的解释,官方描述的太简单,各含义在真实情况如下所示:

  • earliest :当各分区下存在已提交的 offset 时,从提交的 offset 开始消费;无提交的 offset 时,从头开始消费。
  • latest :当各分区下存在已提交的 offset 时,从提交的 offset 开始消费;无提交的 offset 时,消费该分区下新产生的数据。
  • none :topic 各分区都存在已提交的 offset 时,从 offset 后开始消费;只要有一个分区不存在已提交的offset,则抛出异常。

值得一说的是:

  • 指定分区从头消费时,需要了解:一个分区的起始位置是 0 ,但并不代表每时每刻都为 0 ,因为日志清理的动作会清理旧的数据,所以分区的起始位置会自然而然地增加。
  • 指定分区从末尾消费,需要了解:endOffsets() 方法获取的是将要写入最新消息的位置。如下图中 9 的位置:
  • aa1fcca86df44c03bed5062c744d1b20.png

 

 

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Spring Boot可以通过使用Kafka提供的API来指定offset消费消息。 首先,我们需要添加Kafka客户端的依赖。可以在pom.xml文件添加以下依赖: ```xml <dependency> <groupId>org.springframework.kafka</groupId> <artifactId>spring-kafka</artifactId> </dependency> ``` 接下来,我们需要配置Kafka的连接信息。可以在application.properties文件(或application.yml)添加以下配置: ``` spring.kafka.bootstrap-servers=127.0.0.1:9092 spring.kafka.consumer.group-id=your-group-id spring.kafka.consumer.auto-offset-reset=earliest ``` 其,`spring.kafka.bootstrap-servers`指定Kafka服务器的地址和端口,`spring.kafka.consumer.group-id`指定消费者组的ID,`spring.kafka.consumer.auto-offset-reset`指定消费者在消费消息时的起始位置,这里设置为最早的offset。 然后,我们可以编写一个Kafka消费者来指定offset进行消费。可以创建一个Spring Bean来实现Kafka的消息监听器: ```java @Component public class KafkaConsumer { @KafkaListener(topics = "your-topic-name") public void listen(ConsumerRecord<String, String> record) { // 处理消息逻辑 System.out.println("Received message: " + record.value()); } } ``` 在上述代码,`@KafkaListener`注解指定了要监听的topic名称。当有新的消息到达时,会调用`listen`方法进行处理。 如果需要指定offset进行消费,可以在`listen`方法添加`@Header`注解,来获取消息的offset值: ```java @KafkaListener(topics = "your-topic-name") public void listen(ConsumerRecord<String, String> record, @Header(KafkaHeaders.OFFSET) long offset) { // 获取消息的offset值 System.out.println("Received message at offset " + offset + ": " + record.value()); } ``` 以上就是使用Spring Boot和Kafka指定offset消费消息的基本步骤。通过上述配置和代码,我们可以实现具有指定offset功能的Kafka消息消费

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值