kafka 通过消费者获取__consumer_offsets topic的元数据内容

kafka 通过消费者获取__consumer_offsets topic的元数据内容

工作中遇到一个问题需要获取kafka的元数据信息,诸如topic创建信息,消费者消费topic的信息等。要获取kafka的元数据信息,首先想到找zookeeper,利用zookeeper的watcher机制去监听kafka的元数据节点的创建,进而拿到对应信息。但由于kafka新版本存在两种消费者元数据保存机制,因此我们不能只考虑获取zookeeper上的元数据。

kafka 消费者offset存储方式

在kafka 0.9 版本开始提供了新的consumer及consumer group,位移的管理与保存机制发生了很大的变化,新版本的consumer默认将不再保存位移到zookeeper中,而是__consumer_offsets topic中

  • 如果消费者根据java api来消费,,也就是kafka.javaapi.consumer.ConsumerConnector,通过配置参数zookeeper.connect来消费。这种情况,消费者的offset会更新到zookeeper的/kafka/consumers/<group.id>/offsets/<topic>/<partitionId>,但是zookeeper其实并不适合进行大批量的读写操作,尤其是写操作。
  • 因此kafka提供了另一种解决方案:增加__consumeroffsets topic,将offset信息写入这个topic,摆脱对zookeeper的依赖(指保存offset这件事情)。__consumer_offsets中的消息保存了每个consumer group某一时刻提交的offset信息。

如下图,可能有这两种情况ZK:zookeeper,KF:kafka
这里写图片描述

通过新建一个消费者去获取该topic的信息

既然__consumer_offsets 是一个topic,kafka将consumer的消费信息push到该topic,便想到通过消费者的方式去获取该topic的信息

代码如下:

import kafka.common.OffsetAndMetadata;
import kafka.coordinator.*;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;

import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.Properties;

/**
 * @author: wangjian
 * @date: 2018-08-01 15:19
 */
public class KafkaCli {
    public static void main(String[] args) {
        Properties props = new Properties();

        props.put("bootstrap.servers", "cdh003:9092");
        //每个消费者分配独立的组号
        props.put("group.id", "test_1");

        //如果value合法,则自动提交偏移量
        props.put("enable.auto.commit", "true");

        //设置多久一次更新被消费消息的偏移量
        props.put("auto.commit.interval.ms", "1000");

        //设置会话响应的时间,超过这个时间kafka可以选择放弃消费或者消费下一条消息
        props.put("session.timeout.ms", "30000");

        //该参数表示从头开始消费该主题
        props.put("auto.offset.reset", "earliest");

        //注意反序列化方式为ByteArrayDeserializer
        props.put("key.deserializer",
                "org.apache.kafka.common.serialization.ByteArrayDeserializer");
        props.put("value.deserializer",
                "org.apache.kafka.common.serialization.ByteArrayDeserializer");

        KafkaConsumer<byte[], byte[]> consumer = new KafkaConsumer<>(props);

        consumer.subscribe(Collections.singletonList("__consumer_offsets"));

        System.out.println("Subscribed to topic " + "__consumer_offsets");

        while (true) {
            ConsumerRecords<byte[], byte[]> records = consumer.poll(100);
            for (ConsumerRecord<byte[], byte[]> record : records) {
//                这里直接将record的全部信息写到System.out打印流中
//                GroupMetadataManager.OffsetsMessageFormatter formatter = new GroupMetadataManager.OffsetsMessageFormatter();
//                formatter.writeTo(record, System.out);

                //对record的key进行解析,注意这里的key有两种OffsetKey和GroupMetaDataKey
                //GroupMetaDataKey中只有消费者组ID信息,OffsetKey中还包含了消费的topic信息
                BaseKey key = GroupMetadataManager.readMessageKey(ByteBuffer.wrap(record.key()));
                if (key instanceof OffsetKey) {
                    GroupTopicPartition partition = (GroupTopicPartition) key.key();
                    String topic = partition.topicPartition().topic();
                    String group = partition.group();

                    System.out.println("group : " + group + "  topic : " + topic);
                    System.out.println(key.toString());
                } else if (key instanceof GroupMetadataKey) {
                    System.out.println("groupMetadataKey:------------ "+key.key());
                }

                //对record的value进行解析
                OffsetAndMetadata om = GroupMetadataManager.readOffsetMessageValue(ByteBuffer.wrap(record.value()));
                //System.out.println(om.toString());
            }
        }
    }
}

key的解析输出信息如下:

这里写图片描述

value的解析输出信息如下:

这里写图片描述

但是使用这种方式解析过一会儿会出现如下异常:

Exception in thread "main" org.apache.kafka.common.protocol.types.SchemaException: Error reading field 'metadata': Error reading string of length 25970, only 12 bytes available
    at org.apache.kafka.common.protocol.types.Schema.read(Schema.java:72)
    at kafka.coordinator.GroupMetadataManager$.readOffsetMessageValue(GroupMetadataManager.scala:958)
    at kafka.coordinator.GroupMetadataManager.readOffsetMessageValue(GroupMetadataManager.scala)
    at com.wangjian.KafkaCli.main(KafkaCli.java:68)

测试发现这是当key类型为GroupMetadataKey时,去解析OffsetAndMetadata会抛异常,应该用如下方法去解析GroupMetadata:

if (key instanceof GroupMetadataKey) {  
    System.out.println("groupMetadataKey: " + key.key());    
//第一个参数为group id,先将key转换为GroupMetadataKey类型,再调用它的key()方法就可以获得group id
    GroupMetadata groupMetadata = GroupMetadataManager.readGroupMessageValue(((GroupMetadataKey) key).key(), ByteBuffer.wrap(record.value()));

    System.out.println("GroupMetadata: "+groupMetadata.toString());
}

它的输出如下:

groupMetadataKey: test_1
GroupMetadata: [test_1,Some(consumer),Stable,Map(consumer-1-2b952983-41bd-4bdb-bc65-89ceedd91d26 -> [consumer-1-2b952983-41bd-4bdb-bc65-89ceedd91d26,consumer-1,/172.18.89.153,30000])]

可以看到GroupMetadata中保存了该消费者组中每个消费者的具体信息,包括了消费者所在IP等

使用formatter方式写到System.out输出信息如下:

    GroupMetadataManager.OffsetsMessageFormatter formatter = new GroupMetadataManager.OffsetsMessageFormatter();
    formatter.writeTo(record, System.out);

这里写图片描述

这是完整的OffsetMessage信息

同时也可以看到__consumer_offsets topic的每一日志项的格式都是:[Group, Topic, Partition]::[OffsetMetadata[Offset, Metadata], CommitTime, ExpirationTime]

总结:

kafka push 到__consumer_offsets 该topic的元数据信息有两种:

key:OffsetKey —————————> value:OffsetAndMetadata 保存了消费者组各个partition的offset位移信息元数据

key:GroupMetadataKey ————————> value:GroupMetadata 保存了消费者组中各个消费者的信息

在通过java 开发consumer去消费该topic获取元数据时,应注意区分这两种情况,还有反序列化方式

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值