Kafka监控:consumer消费进度查询方案的演变

1. 背景

大数据平台经常以Kafka作为消息中间件,且需要有完整的针对Kafka的管理和监控体系,例如实时查看:

committed-offset:topic在不同消费者组中的消费进度

visible-offset: topic中的可见消息总量,当consumer的隔离级别为read_uncommitted,visible-offset等于high watermark;当consumer的隔离级别为read_committed,visible-offset等于last stable offset

Lag:消费延迟,lag=logEndOffset - committedOffset
log-end-offset:日志末端位移,代表日志文件中下一条待写入消息的offset,这个offset上实际是没有消息的

消费进度的保存机制在不同的Kafka版本中经历了以下几个阶段的变化:

<=0.8:保存在zookeeper中的/consumer路径下

>=0.9:保存在内部主题__consumer_offsets中

因为zookeeper是一个分布式协调工具,不适合大量数据的读和写,因此将消费进度转而保存在内部主题__consumer_offsets中是一个合适的优化,我们只针对该种保存方式进行查询。

2. 方案一:__consumer_offsets

既然主题__consumer_offsets中保存了消费进度消息,我们的第一想法是通过查询__consumer_offsets来获取消费进度。

2.1 消息格式

要想正确地解析__consumer_offsets并获取消费进度,我们首先要了解__consumer_offsets中消息格式。

__consumer_offsets 中保存的记录是普通的Kafka消息,但是消息的格式由Kafka来维护,用户不能干预。__consumer_offsets中保存三类消息,分别是:

  1. Consumer group组元数据消息

每个消费者组的元信息都保存在这个topic中,这些元数据包括:
在这里插入图片描述
需要强调的是,如果使用simple consumer(consumer.assign),因为不存在消费者组,则不会向该内部主题写入消息。

key:key是一个二元组,格式是【version+groupId】,这里的版本表征这类消息的版本号,无实际用途;

value:图中的组元数据

写入时机:消费者组rebalance时写入的

  1. Consumer group位移消息

__consumer_offsets保存consumer提交到Kafka的位移数据,这是众所周知的。元数据如下:
在这里插入图片描述

其中,过期时间是用户指定的过期时间。如果consumer代码在提交位移时没有明确指定过期间隔,故broker端默认设置过期时间为提交时间+offsets.retention.minutes参数值,即提交1天之后自动过期。Kafka会定期扫描_consumer_offsets中的位移消息并删除掉那些过期的位移数据。

key:一个三元组,格式是【groupId + topic + partition】

value:图中的位移元数据

写入时机:消费者提交位移时写入

提交的时候,即使位移数据并没有更新,也会向__consumer_offsets写入一条新消息

  1. Tombstone消息

第三类消息是tombstone消息或delete mark消息。这类消息只出现在源码中而不暴露给用户。

写入时机:在Kafka后台线程扫描并删除过期位移或者__consumer_offsets分区副本重分配时写入的

2.2 代码
    /**
     * GroupTopicPartition --> OffsetAndMetadata
     * 利用guava cache清除策略进行group的过期清除,利用监听器同时清除集合中的group
     */
    private final Cache<GroupTopicPartition, OffsetAndMetadata> groupTopicPartitionOffsetMap = CacheBuilder
                    .newBuilder()
                    .maximumSize(100000)
                    .expireAfterAccess(7,TimeUnit.DAYS)
                    .removalListener((t) ->  groupTopicPartitionOffsetSet.remove(t))
                    .build();

    /**
     * group topicPartition set ,element: group、topic、partition
     */
    private Set<GroupTopicPartition> groupTopicPartitionOffsetSet = Collections.newSetFromMap(new ConcurrentHashMap<>());

    private Map<String,Set<String>> consumerTopicSetMap = new HashMap<>(32);

    GroupMetadataManager groupMetaManager = new GroupMetadataManager();

    @Override
    public void run() {
   
        // 自定义一个消费者组名为“kafkaManager”的消费者,订阅内部topic获取offset信息
        Consumer<byte[],byte[]> consumer = createKafkaConsumer();
        consumer.subscribe(Collections.singleton("__consumer_offsets"));

        while (true){
   
           ConsumerRecords<byte[],byte[]> records = consumer.
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值