You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过Spring Kafka获取指定Kafka Topic的消费者延迟?

实现Spring Kafka多Topic消费者指定Topic的未消费消息数

问题分析

你之前尝试的metrics方式无法满足需求,因为records-lag指标通常按消费者实例或分区聚合,没有直接关联到具体Topic,没法精准过滤出单个Topic的未消费消息数。

解决方案

计算指定Topic的consumer lag,核心是对比消费者组已提交的偏移量和Topic分区的最新末端偏移量,累加对应分区的差值即可。

1. 配置AdminClient

首先配置Kafka AdminClient,用于获取消费者组和Topic的元数据:

@Configuration
public class KafkaConfig {

    @Value("${spring.kafka.bootstrap-servers}")
    private String bootstrapServers;

    @Bean
    public AdminClient adminClient() {
        Map<String, Object> config = new HashMap<>();
        config.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
        return AdminClient.create(config);
    }
}

2. 实现lag计算方法

通过AdminClient获取所需数据,实现getUnconsumedMessagesCount方法:

import org.apache.kafka.clients.admin.AdminClient;
import org.apache.kafka.clients.admin.ConsumerGroupDescription;
import org.apache.kafka.clients.admin.ListConsumerGroupOffsetsResult;
import org.apache.kafka.clients.admin.OffsetSpec;
import org.apache.kafka.clients.admin.TopicDescription;
import org.apache.kafka.common.TopicPartition;
import org.springframework.kafka.config.KafkaListenerEndpointRegistry;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

@Component
public class KafkaLagService {

    private final AdminClient adminClient;
    private final KafkaListenerEndpointRegistry endpointRegistry;

    public KafkaLagService(AdminClient adminClient, KafkaListenerEndpointRegistry endpointRegistry) {
        this.adminClient = adminClient;
        this.endpointRegistry = endpointRegistry;
    }

    public int getUnconsumedMessagesCount(String topicName) throws ExecutionException, InterruptedException {
        // 从监听容器获取消费者组ID
        String groupId = endpointRegistry.getListenerContainer("errorTopicsConsumer")
                .getGroupId();

        // 获取消费者组关联的分区(消费者已启动时)
        ConsumerGroupDescription groupDesc = adminClient.describeConsumerGroups(Collection.of(groupId))
                .describedGroups()
                .get(groupId)
                .get();

        Collection<TopicPartition> topicPartitions = groupDesc.members().stream()
                .flatMap(member -> member.assignment().topicPartitions().stream())
                .filter(tp -> tp.topic().equals(topicName))
                .collect(Collectors.toList());

        // 消费者未启动时,直接获取Topic的所有分区
        if (topicPartitions.isEmpty()) {
            TopicDescription topicDesc = adminClient.describeTopics(Collection.of(topicName))
                    .all()
                    .get()
                    .get(topicName);
            topicPartitions = topicDesc.partitions().stream()
                    .map(partitionInfo -> new TopicPartition(topicName, partitionInfo.partition()))
                    .collect(Collectors.toList());
        }

        // 获取消费者组已提交的偏移量
        ListConsumerGroupOffsetsResult offsetsResult = adminClient.listConsumerGroupOffsets(groupId);
        Map<TopicPartition, Optional<Long>> committedOffsets = offsetsResult.partitionsToOffsetAndMetadata()
                .get();

        // 获取分区最新末端偏移量
        Map<TopicPartition, Long> endOffsets = adminClient.listOffsets(topicPartitions.stream()
                        .collect(Collectors.toMap(tp -> tp, tp -> OffsetSpec.latest())))
                .all()
                .get();

        // 累加所有分区的lag
        int totalLag = 0;
        for (TopicPartition tp : topicPartitions) {
            long committedOffset = committedOffsets.getOrDefault(tp, Optional.empty()).orElse(0L);
            long endOffset = endOffsets.getOrDefault(tp, 0L);
            totalLag += Math.max(0, endOffset - committedOffset);
        }

        return totalLag;
    }
}

3. 关键说明

  • 若消费者未启动(autoStartup=false),消费者组无活跃成员,此时直接获取Topic所有分区,用组内最新提交的偏移量计算lag。
  • 需处理异步操作的异常(ExecutionException、InterruptedException),实际业务中可根据场景封装或转换。
  • AdminClient配置需与消费者配置保持一致,尤其是bootstrap-servers。

内容的提问来源于stack exchange,提问作者Aryak Deshpande

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 01:09:55