如何获取Kafka的消费者详情——从Scala到Java的切换

54人阅读 评论(0) 收藏 举报
分类:

前文摘要

在上一篇文章《Kafka的Lag计算误区及正确实现》中介绍了如何计算消费者的消费滞后量(Lag),并且讲解了如何调用Kafka的kafka.admin.ConsumerGroupCommand文件中的KafkaConsumerGroupService来发送OffsetRequest和OffsetFetchRequest两个请求,进而通过两个请求结果之间的差值来获得结果。不过如果你不想修改kafka-core的代码并重新编译的话,这种实现方式无法成功,所以本文的主要目的就是通过调用更底层的API来实现不修改kafka-core的代码来实现KafkaConsumerGroupService的功能,即通过Java调用Scala的代码来实现获取Kafka的消费者详情的功能。

目标及实现

实现如同 bin/kafka-consumer-group.sh –describe –bootstrap-server localhost:9092 –group CONSUMER_GROUP_ID的效果:

[root@node2 kafka_2.12-1.0.0]# bin/kafka-consumer-groups.sh --describe --bootstrap-server localhost:9092 --group CONSUMER_GROUP_ID
TOPIC                PARTITION  CURRENT-OFFSET  LOG-END-OFFSET  LAG        CONSUMER-ID                                       HOST                   CLIENT-ID
topic-test1          0          1648            1648            0          CLIENT_ID-e2d41f8d-dbd2-4f0e-9239-efacb55c6261    /192.168.92.1          CLIENT_ID
topic-test1          1          1648            1648            0          CLIENT_ID-e2d41f8d-dbd2-4f0e-9239-efacb55c6261    /192.168.92.1          CLIENT_ID
topic-test1          2          1648            1648            0          CLIENT_ID-e2d41f8d-dbd2-4f0e-9239-efacb55c6261    /192.168.92.1          CLIENT_ID
topic-test1          3          1648            1648            0          CLIENT_ID-e2d41f8d-dbd2-4f0e-9239-efacb55c6261    /192.168.92.1          CLIENT_ID

KafkaConsumerGroupService的核心方法是CollectGroupAssignment,其方法参数为一个consumer group的groupId,方法输出为上面示例中的列表信息。CollectGroupAssignment方法主要有以下几个步骤:

  1. 根据groupId调用describeConsumerGroup方法(内部原理是发送DescribeGroupsRequest请求)来获取consumer group的基本信息,参考上面示例中的CONSUMER-ID、HOST、CLIENT-ID以及TopicPartition信息,但是没有CURRENT-OFFSET、LOG-END-OFFSET、LAG信息。注意这里的LOG-END-OFFSET是消费者可见的LEO,不是生产者可见的LEO,也就是通俗意义上的HW。
  2. 根据groupId调用listGroupOffsets方法(内部原理是发送OffsetFetchRequest请求)来获取各个分区(Partition)的对应的消费位移CURRENT-OFFSET。
  3. 通过调用KafkaConsumer的endOffsets方法来获取TopicPartition对应的HW,即示例中的LOG-END-OFFSET。
  4. 计算Lag并组合成信息列表List<PartitionAssignmentState>。

改造

对应Java版的KafkaConsumerGroupService改造代码可以参见代码,目录结构如下图所示:

其中model中的ConsumerGroupSummary、ConsumerSummary和PartitionAssignmentState是简单的JavaBean, PartitionAssignmentState是用来保存每个TopicPartition的消费者信息的,具体内容参考如下。KafkaConsumerGroupCustomService就是本文所要陈述的Java改造办的KafkaConsumerGroupSerivice,ConsumerGroupUtils用来存放一些公用的代码。

@Data
@Builder
public class PartitionAssignmentState {
    private String group; // groupId
    private Node coordinator; // consumer coodinator节点信息
    private String topic;
    private int partition;
    private long offset;
    private long lag;
    private String consumerId;
    private String host;
    private String clientId;
    private long logEndOffset;
}

初始化KafkaConsumerGroupCustomService需要Kafka的服务端地址,然后初始化AdminClient和KafkaConsumer,AdminClient中包含了众多管理类方法,主要是通过发送各种自定义协议请求来完成,上面步骤中所说的describeConsumerGroup和listGroupOffsets方法也是通过AdminClient来实现的;KafkaConsumer主要是用来获取TopicPartition对应的HW(消费者可见的LogEndOffsets)的。

KafkaConsumerGroupCustomService中与scala版对应的collectGroupAssignment方法如下(详细步骤参考代码注释):

public List<PartitionAssignmentState> collectGroupAssignment(
        AdminClient adminClient, KafkaConsumer<String, String> consumer,
        String group) {
    //1. 获取consumer group的基本信息,包括CONSUMER-ID、HOST、
    // CLIENT-ID以及TopicPartition信息
    AdminClient.ConsumerGroupSummary consumerGroupSummary
            = adminClient.describeConsumerGroup(group, 0);
    List<TopicPartition> assignedTopicPartitions = new ArrayList<>();
    List<PartitionAssignmentState> rowsWithConsumer = new ArrayList<>();
    scala.collection.immutable.List<AdminClient.ConsumerSummary> consumers
            = consumerGroupSummary.consumers().get();
    if (consumers != null) {
        //2. 获取各个分区(Partition)的对应的消费位移CURRENT-OFFSET
        scala.collection.immutable.Map<TopicPartition, Object> offsets
                = adminClient.listGroupOffsets(group);
        if (offsets.nonEmpty()) {
            String state = consumerGroupSummary.state();
            // 3. 还有一个状态是Dead表示"group"对应的consumer group不存在
            if (state.equals("Stable") || state.equals("Empty")
                    || state.equals("PreparingRebalance")
                    || state.equals("AwaitingSync")) {
                List<ConsumerSummary> consumerList = changeToJavaList(consumers);
                // 4. 获取当前有消费者的消费信息,即包含CONSUMER-ID、HOST、CLIENT-ID
                rowsWithConsumer = getRowsWithConsumer(consumerGroupSummary, offsets,
                        consumer, consumerList, assignedTopicPartitions, group);
            }
        }
        //5. 获取当前没有消费者的消费信息
        List<PartitionAssignmentState> rowsWithoutConsumer =
                getRowsWithoutConsumer(consumerGroupSummary,
                offsets, consumer, assignedTopicPartitions, group);
        //6. 合并结果
        rowsWithConsumer.addAll(rowsWithoutConsumer);
    }
    return rowsWithConsumer;
}

KafkaConsumerGroupCustomService类中包含有getRowsWithConsumer()、getRowsWithoutConsumer()、changeToJavaList等私有方法也都是在Scala语言与Java语言之间进行切换,这样可以不需要修改kafka-core的原生代码而通过外部的封装调用既可以实现获取Kafka消费者详情的功能。光看代码比较抽象,建议对此感兴趣的同学可以亲自对比一下kafka-core包中kafka.admin.ConsumerGroupCommand的KafkaConsumerGroupSerivice与笔者自定义的KafkaConsumerGroupCustomService的实现来了解下Scala语言到Java语言的转换。

如果需要打印详情可以调用KafkaConsumerGroupCustomService同目录的ConsumerGroupUtils类中的printPasList(List list)方法。注意要运行这些代码需要JDK8的环境,笔者为了让代码显得“骚气”一点就用来一点Java8的语法,如果需要Java7的代码实现可以关注私聊。

或许有些同学对于Scala和Java交叉的代码并不感冒,想要寻求一种存Java式的实现方式,那么在这里怎么实现呢?答案是通过KafkaAdminClient,它是AdminClient的Java版实现,从Kafka0.11.0.0版本开始引入的,不过KafkaAdminClient本身并没有提供describeConsumerGroup、listGroupOffsets之类的方法给我们直接使用,扩展一下也很方便,由于篇幅限制,这部分的内容将在下一篇文章中进行介绍,如果想要先一睹为快,可以参考下代码实现,详细的逻辑解析敬请期待….


欢迎支持《RabbitMQ实战指南》以及关注微信公众号:朱小厮的博客。

查看评论

Kafka 生产者和消费者 demo (java&scala)

前几天完成了kafka ubuntu单机的搭建,后来就尝试写写kafka的简单代码,网上也有很多例子,但是如果自己编写运行还是有一些坑在里面,我也简单记录以下自己遇到的问题。...
  • zhumingyuan111
  • zhumingyuan111
  • 2017-06-30 22:39:10
  • 1968

Kafka High Level Consumer API in Scala

本文目的研究了一下Kafka Produce/Consumer 的API,发现Consumer API的使用并没有那么的straight forward。折腾了2天后,终于摸到了一些门道,这里记录下怎...
  • u011491148
  • u011491148
  • 2015-06-26 14:13:40
  • 2857

java实现Kafka的消费者示例

使用java实现Kafka的消费者 1 2 3 4 5 6 7 8 9 10 11 12 13 ...
  • chenjieit619
  • chenjieit619
  • 2016-10-28 14:41:08
  • 1499

kafka集群搭建和使用Java写kafka生产者消费者

转载自:http://chengjianxiaoxue.iteye.com/blog/2190488 1 kafka集群搭建   Java代码   ...
  • u011026968
  • u011026968
  • 2015-08-24 14:28:12
  • 5011

kafka源码解析之十七消费者流程(客户端如何获取topic的数据)

Kafka消费数据的角色分为普通消费者和高级消费者,其介绍如下: 16.1 普通消费者 特点:1)一个消息读取多次    2)在一个处理过程中只消费某个broker上的partition的部分消息 ...
  • wl044090432
  • wl044090432
  • 2016-04-11 19:53:08
  • 8749

kafka学习笔记 --- Scala实现Kafka producer 和 consumer

kafka学习笔记 --- Scala实现Kafka producer 和 consumer
  • u012965373
  • u012965373
  • 2017-07-06 14:58:48
  • 3664

scala发送消息到kafka示例

1) build文件需要填写依赖的jar包 "org.slf4j" % "slf4j-api" % "1.7.5", "org.slf4j" % "slf4j-log...
  • qingye2008
  • qingye2008
  • 2016-03-29 13:54:04
  • 1589

kafka 消费者offset记录位置和方式

原文:http://www.mamicode.com/info-detail-1969443.html kafka消费者在会保存其消费的进度,也就是offset,存储的位置根据选用的kafk...
  • u013063153
  • u013063153
  • 2017-09-28 11:52:12
  • 1468

kafka-java-demo 基于java的kafka生产消费者示例

  • 2017年11月24日 15:14
  • 59KB
  • 下载

关于怎么获取kafka指定位置offset消息

1.在kafka中如果不设置消费的信息的话,一个消息只能被一个group.id消费一次,而新加如的group.id则会被“消费管理”记录,并指定从当前记录的消息位置开始向后消费。如果有段时间消费者关闭...
  • u014104286
  • u014104286
  • 2017-08-11 22:35:20
  • 7349
    最新资讯
    欢迎支持《RabbitMQ实战指南》 and 关注微信公众号:朱小厮的博客。
    个人资料
    专栏达人 持之以恒
    等级:
    访问量: 117万+
    积分: 1万+
    排名: 1325
    最新评论
    博客专栏