Kafka 根据 时间段查询消费数据

 1.主要逻辑:

    private Map<TopicPartition, OffsetAndTimestamp> getTimestampOffset(KafkaConsumer<String, byte[]> kafkaConsumer, String topic, Date date){
        Map<TopicPartition, Long> startMap = new HashMap<>();
        List<PartitionInfo> partitionInfos = kafkaConsumer.partitionsFor(topic);
        for (PartitionInfo partitionInfo : partitionInfos) {
            int partition = partitionInfo.partition();
            TopicPartition topicPartition = new TopicPartition(topic, partition);
            startMap.put(topicPartition, date.getTime());
        }
        return kafkaConsumer.offsetsForTimes(startMap);
    }


    @SneakyThrows
    private void execute(KafkaConsumer<String, byte[]> kafkaConsumer, Date startTime, Date endTime){
        Map<TopicPartition, OffsetAndTimestamp> startOffsetMap = getTimestampOffset(kafkaConsumer, topic, startTime);
        Map<TopicPartition, OffsetAndTimestamp> endOffsetMap = getTimestampOffset(kafkaConsumer, topic, endTime);
        log.info("topic {}, start offset {}, end offset {}", topic, startOffsetMap, endOffsetMap);
        List<PartitionInfo> partitionInfos = kafkaConsumer.partitionsFor(topic);
        List<TopicPartition> partitionList = new ArrayList<>();
        for (PartitionInfo partitionInfo : partitionInfos) {
            partitionList.add(new TopicPartition(topic, partitionInfo.partition()));
        }
        Map<Integer, Long> partitionMap;
        kafkaConsumer.assign(partitionList);
        for (Map.Entry<TopicPartition, OffsetAndTimestamp> entry : startOffsetMap.entrySet()) {
            kafkaConsumer.seek(entry.getKey(), entry.getValue().offset());
        }
        Set<Integer> partitionSet;
        if (endOffsetMap != null){
            partitionMap = endOffsetMap.entrySet().stream().collect(Collectors.toMap(topicPartitionOffsetAndTimestampEntry -> topicPartitionOffsetAndTimestampEntry.getKey().partition(), topicPartitionOffsetAndTimestampEntry -> {
                OffsetAndTimestamp value = topicPartitionOffsetAndTimestampEntry.getValue();
                if (value != null){
                    return value.offset();
                }else{
                    return Long.MAX_VALUE;
                }
            }, (t1, t2) -> t1));
            partitionSet = partitionMap.entrySet().stream().map(Map.Entry::getKey).collect(Collectors.toSet());
        }else{
            partitionMap = Collections.emptyMap();
            partitionSet = Collections.emptySet();
        }
        
        while(true){
            Map<Integer, Long> partitionOffsetMap = new HashMap<>();
            ConsumerRecords<String, byte[]> consumerRecords = kafkaConsumer.poll(Duration.ofMillis(10000));
            for (ConsumerRecord<String, byte[]> consumerRecord : consumerRecords) {
                partitionOffsetMap.compute(consumerRecord.partition(), (integer, aLong) -> aLong == null ? consumerRecord.offset() : Math.max(aLong, consumerRecord.offset()));
                long timestamp = consumerRecord.timestamp();
                if (timestamp < startTime.getTime() || timestamp > endTime.getTime()){
                    continue;
                }
                byte[] value = consumerRecord.value();
                //todo 消费逻辑
            }
            for (Map.Entry<Integer, Long> entry : partitionOffsetMap.entrySet()) {
                Integer partition = entry.getKey();
                Long maxOffset = entry.getValue();
                Long endOffset = partitionMap.get(partition);
                if (endOffset != null && maxOffset > endOffset && !partitionSet.isEmpty()){
                    partitionSet.remove(partition);
                }
            }
            if (consumerRecords.isEmpty() || (!partitionMap.isEmpty() && partitionSet.isEmpty())){
                break;
            }
        }
    }

2.调用逻辑

    public void run(){
        //添加kafka配置
        Map<String, Object> kafkaConfig = new HashMap<>();
        KafkaConsumer<String, byte[]> kafkaConsumer = new KafkaConsumer<>(kafkaConfig);
        execute(kafkaConsumer, startTime, endTime);
    }

3. 说明

在自定义kafka查询时(配合seek使用),需用kafkaConsumer.assign,而不是kafkaConsumer.subscribe
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
对于Kafka消费异常重新消费的问题,有几种常见的处理方式: 1. 手动提交偏移量(Manual Offset Commit):在消费者应用程序中,可以手动管理消费的偏移量,当消费异常发生时,可以将偏移量回滚到异常发生之前的位置,并重新消费消息。这种方式需要开发者自己实现偏移量管理和重新消费的逻辑。 2. 定期提交偏移量(Periodic Offset Commit):消费者应用程序可以定期提交偏移量,例如每隔一段时间或每消费一定数量的消息后提交偏移量。这样即使发生异常,下次启动时可以从上一次提交的偏移量处继续消费。 3. 使用seek()方法重新定位偏移量:Kafka提供了seek()方法,可以手动将消费者的偏移量定位到指定位置。当发生异常时,可以使用此方法将偏移量重新定位到异常发生之前的位置,并重新消费。 4. 使用Kafka Connect消费者重置偏移量(Consumer Offset Reset):Kafka Connect是Kafka提供的一种分布式数据集成工具,其中包含了一些内置的消费者重置偏移量的功能。通过配置相应的参数,可以将消费者的偏移量重置为最早或最新的位置,从而重新消费消息。 需要根据具体的场景和需求选择适合的处理方式,并结合消费者应用程序的逻辑来实现重新消费的功能。同时,为了保证消息的有序性和避免数据重复消费,还需要考虑幂等性处理和消息去重等机制。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值