作者:vivo 互联网大数据团队- Chen Jianbo

本文是《vivo Pulsar万亿级消息处理实践》系列文章第3篇。

Pulsar是Apache基金会的开源分布式流处理平台和消息中间件,它实现了Kafka的协议,可以让使用Kafka API的应用直接迁移至Pulsar,这使得Pulsar在Kafka生态系统中更加容易被接受和使用。KoP提供了从Kafka到Pulsar的无缝转换,用户可以使用Kafka API操作Pulsar集群,保留了Kafka的广泛用户基础和丰富生态系统。它使得Pulsar可以更好地与Kafka进行整合,提供更好的消息传输性能、更强的兼容性及可扩展性。vivo在使用Pulsar KoP的过程中遇到过一些问题,本篇主要分享一个分区消费指标缺失的问题。

系列文章:

  1.  vivo Pulsar万亿级消息处理实践(1)-数据发送原理解析和性能调优
  2.  vivo Pulsar万亿级消息处理实践(2)-从0到1建设Pulsar指标监控链路

文章太长?1分钟看图抓住核心观点👇

vivo Pulsar 万亿级消息处理实践(3)-KoP指标异常修复_Pulsar

一、问题背景

在一次版本灰度升级中,我们发现某个使用KoP的业务topic的消费速率出现了显著下降,具体情况如下图所示:

vivo Pulsar 万亿级消息处理实践(3)-KoP指标异常修复_Java_02

什么原因导致正常的升级重启服务器会出现这个问题呢?直接查看上报采集的数据报文:

kop_server_MESSAGE_OUT{group="",partition="0",tenant="kop",topic="persistent://kop-tenant/kop-ns/service-raw-stats"} 3
kop_server_BYTES_OUT{group="",partition="0",tenant="kop",topic="persistent://kop-tenant/kop-ns/service-raw-stats"} 188
  • 1.
  • 2.

我们看到,KoP消费指标kop\_server\_MESSAGE

\_OUT、kop\_server\_BYTES\_OUT是有上报的,但指标数据里的group标签变成了空串(缺少消费组名称),分区的消费指标就无法展示了。是什么原因导致了消费组名称缺失?

二、问题分析

1、找到问题代码

我们去找下这个消费组名称是在哪里获取的,是否逻辑存在什么问题。根据druid中的kop\_subscription对应的消费指标kop\server

MESSAGE\_OUT、kop\_server\_BYTES\_OUT,找到相关代码如下:

private void handleEntries(final List<Entry> entries,
                               final TopicPartition topicPartition,
                               final FetchRequest.PartitionData partitionData,
                               final KafkaTopicConsumerManager tcm,
                               final ManagedCursor cursor,
                               final AtomicLong cursorOffset,
                               final boolean readCommitted) {
....
        // 处理消费数据时,获取消费组名称
        CompletableFuture<String> groupNameFuture = requestHandler
                .getCurrentConnectedGroup()
                .computeIfAbsent(clientHost, clientHost -> {
                    CompletableFuture<String> future = new CompletableFuture<>();
                    String groupIdPath = GroupIdUtils.groupIdPathFormat(clientHost, header.clientId());
                    requestHandler.getMetadataStore()
                            .get(requestHandler.getGroupIdStoredPath() + groupIdPath)
                            .thenAccept(getResultOpt -> {