---
title: "Kafka 指标非得这么麻烦吗？"
date: 2026-05-26T00:00:00.000Z
author: "grzegorz"
excerpt: "为什么 Kafka 指标如此令人头疼，为什么 JMX 仪表盘常常显示不出数据，以及如何通过 OTLP 原生导出 Kafka 指标。"
---
如今，[Apache Kafka](https://kafka.apache.org/) 是 IT 领域使用最广泛的软件之一。它无处不在，是事件流处理的事实标准，并在大型企业中被广泛采用。Apache Kafka 项目称，Kafka 被数以千计的组织使用，并受到超过 80% 的财富 100 强公司的信赖。

在许多组织中，Kafka 是其系统的骨干，而 Kafka 生态系统也在持续壮大。

但运行一个可靠的 Kafka 集群可能颇具挑战：它在 broker 和 topic 两个层面都有大量配置项，它是分布式的，而且我们显然希望它高可用。

监控至关重要：如果没有一套合适的指标采集、仪表盘构建乃至告警定义机制，运维团队就是盲目的。

在本文中，我将描述 Kafka 指标已知的种种问题，并提出一个有望解决其中大部分——甚至全部——问题的方案。

> 你是否曾导入一个 Grafana Kafka 仪表盘，却发现它不显示任何数据？答案就在下文中。

## 那么，Kafka 究竟是什么？

我敢打赌，如果你正在读这篇文章，你一定非常清楚 Kafka 是什么、它的用途是什么。但这里还是快速回顾一下：

Apache Kafka 是一个分布式事件流处理平台。最简单的理解方式是：Kafka 是一个持久化的、只追加的日志，它让许多应用程序——producer——向其中写入事件，又让另外许多应用程序——consumer——以极高的吞吐量实时读回这些事件。

事件存在于 topic 中，topic 被切分为 partition，并在多个 broker（构成 Kafka 集群的服务器）之间复制。这带来了横向可扩展性和容错能力：如果一个 broker 宕机，另一个副本会接管，consumer 可以继续读取而不会漏掉任何内容。

如今，Kafka 处于大量关键业务系统的核心：支付流水线、欺诈检测、微服务通信、数据库变更数据捕获、日志聚合、IoT 遥测。当它运转良好时，没人会注意到。一旦它出问题，那就是灾难。

## Kafka 内部的指标系统——问题的根源

你知道 Kafka broker 是通过两套不同的指标系统暴露指标的吗？我以前不知道。

Kafka 指标由运行在同一个 JVM 中的两个独立指标库处理。

较旧的那个是 Yammer Metrics。它从早期就存在于 Kafka 中，许多基础的 broker 指标——例如 `BytesInPerSec`、`MessagesInPerSec` 和 `UnderReplicatedPartitions`——至今仍由它处理。

第二个是 Kafka Metrics，即 `org.apache.kafka.common.metrics`，也称为 SPI——Service Provider Interface。它是在创建 Java 客户端时引入的，也被 Kafka Streams 和 Kafka Connect 等生态系统工具所使用。

这不只是冷知识。官方的 [Apache Kafka 监控文档](https://kafka.apache.org/documentation/#monitoring)指出，Kafka 对服务器指标使用 Yammer Metrics，而 Java 客户端使用 Kafka Metrics。两者都通过 JMX 暴露指标，并且都可以用可插拔的 stats reporter 进行配置。

这两套系统的存在是出于历史原因。在某个阶段，所有新指标都被创建为 SPI 指标，但并没有把 Yammer 指标迁移到 SPI 的计划。

以下是主要区别：

| 方面 | Yammer Metrics MBeans | Kafka Metrics MBeans |
|---|---|---|
| 在 Kafka 中的主要用途 | 经典的 broker/server/controller 指标 | Java 客户端以及较新的/通用的 broker/controller 模块 |
| Reporter 配置 | `kafka.metrics.reporters` | `metric.reporters` |
| Reporter 接口 | `kafka.metrics.KafkaMetricsReporter` | `org.apache.kafka.common.metrics.MetricsReporter` |
| 默认 JMX 暴露 | Yammer JMX reporter | `org.apache.kafka.common.metrics.JmxReporter` |
| MBean 形态 | 指标名通常是 `ObjectName` 的一部分，以 `name=...` 形式出现 | `ObjectName` 通常是 domain + type + tags；指标名通常是属性 |
| 属性 | 通用的 Yammer 属性，如 `Value`、`Count`、`MeanRate`、`OneMinuteRate`、百分位数 | 以 Kafka 指标名作为属性，例如 `byte-rate`、`throttle-time`、`connection-count`、`*-rate`、`*-total` |
| 示例风格 | `kafka.server:type=BrokerTopicMetrics,name=BytesInPerSec,topic=my-topic` | `kafka.server:type=Produce,user=alice,client-id=app1`，带有 `byte-rate`、`throttle-time` 等属性 |

## Kafka 指标设置——老办法

监控 Kafka 的传统做法是用 [Prometheus JMX Exporter](https://prometheus.github.io/jmx_exporter/) 从 JMX 导出指标。它是一个以 Java agent 形式运行的库，通过一个规则文件和一个端口号进行配置。

该端口被用作 Prometheus 端点，后面附加 `/metrics`。

那规则文件呢？它是一个庞大的 YAML 文件，里面塞满了大量正则表达式。通常没人完全理解其中的细节，大家只是照用，寄希望于当初写它的人确实知道自己在做什么。

于是，你必须把 JMX Exporter 上传到所有 broker，可能还要创建或编辑带有规则的配置文件——祝你好运——然后重启 broker。如果一切顺利，你可以 curl 指标端点并看到指标。很好！

接着你把这些指标抓取到 [Prometheus](https://prometheus.io/) 中，在 [Grafana](https://grafana.com/) 里下载一些 Kafka 仪表盘，然后……在大多数情况下，你看到的是「No Data」。为什么？因为规则没有任何统一的标准，所以唯一的选择就是编辑仪表盘，或者尝试再找一个。

但事情还没完：这两套指标系统中的每一个都可以用一个自定义的 class（即插件）来配置。看看上面的表格——是的，有两个设置项。一个带 `kafka` 前缀，另一个不带。一个是单数的 `metric`，另一个是复数的 `metrics`。

没错，就是这样。

## 嘿，都 2026 年了，Kafka 能用 OpenTelemetry 吗？

简短的回答是：不能直接用。但确实有一些办法可以让 Kafka 学会 [OpenTelemetry](https://opentelemetry.io/) 协议。

有几种做法：

- JMX Exporter 加上 [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) 中的 Prometheus receiver——与上文描述的 JMX 映射问题如出一辙。
- [OpenTelemetry Collector JMX receiver](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/jmxreceiver)——仍然基于 JMX，需要暴露 JMX，而且该组件现已被标记为弃用，并建议改用一个独立的 JMX Gatherer Java 程序。
- [OpenTelemetry Java agent](https://opentelemetry.io/docs/zero-code/java/agent/)——一个不错的选择。它可以通过 JMX Metric Insight 模块采集 Kafka broker 指标，OpenTelemetry demo 对 Kafka 就采用了这种方式。但如果你想要一组自定义的 JMX 指标，最终你还是会落到又一个指标映射文件上。

## 解决方案

如果我们有一样东西，能够解决上文描述的所有问题，会怎么样？

如前所述，`metric.reporters` 和 `kafka.metrics.reporters` 设置仅仅是 class 名。这些 class 必须分别实现接口 `org.apache.kafka.common.metrics.MetricsReporter` 和 `kafka.metrics.KafkaMetricsReporter`。

于是我们有了一个想法：一个具备以下特性的库。

### 原生 OTLP，无需 JMX 中转

大多数 Kafka 可观测性技术栈会以 JVM agent 的形式挂上 `jmx_exporter` 之类的东西，然后通过 HTTP 端点抓取 MBean，再推送到 collector。

这个插件运行在 Kafka 进程内部，并直接用 [OTLP](https://opentelemetry.io/docs/specs/otlp/) 与 collector 通信——少一个进程，少一处配置面，也无需维护 JMX 规则 YAML。

### 一个插件，两套 Kafka registry

Kafka broker 通过两套并行的系统暴露指标：用 `metric.reporters` 配置的 Kafka SPI，以及遗留的 Yammer/Coda Hale registry。

若干 broker 内部信号——`UnderReplicatedPartitions`、`OfflinePartitionsCount`、`ActiveControllerCount` 以及按 topic 划分的 `BrokerTopicMetrics`——只在 Yammer 中注册。

`OtlpMetricReporter` 通过单一配置同时挂接到两者。同一个 JAR 在客户端上也可原样运行，此时 Yammer 一侧会自动禁用。

### 设计上的故障安全——Kafka 永不被阻塞

诸如 `metricChange` 和 `metricRemoval` 之类的指标回调只会触碰一个内存中的 `ConcurrentHashMap`。所有 I/O 都发生在一个守护调度线程上。

如果 collector 不可达，导出调用会超时，该批次会被丢弃，下一个 tick 重新开始。没有重试队列，没有无界内存，也不会影响 Kafka 的 produce/fetch 延迟。

### Broker 上下文成为一等的 Prometheus 标签

Kafka 会以 cluster id、node/broker id 和 Kafka 版本调用 `MetricsReporter.contextChange(MetricsContext)`。

该插件捕获这些值，并将它们作为 OTLP 资源属性附加上去。它们会以标签形式出现——`kafka_cluster_id`、`kafka_node_id` 或 `kafka_broker_id`——出现在每一条时间序列上，因此 `by(kafka_cluster_id, kafka_node_id)` 在 PromQL 中开箱即用，无需任何额外接线。

## 女士们，先生们，请认识一下 `monedula-metrics-reporter`

[`monedula-metrics-reporter`](https://github.com/monedula-dev/monedula-metrics-reporter) 是一个满足上述需求——并且不止于此——的开源库。

它使用 gRPC 或 HTTP 直接通过 OTLP 导出 Kafka 指标，在 Java 17+ 上支持 Kafka 3.x 和 4.x，并且还能在同一条流水线上发出 JVM 运行时指标。

它还包含一些实用的生产特性，例如：

- 指标白名单，
- 自定义资源属性，
- TLS 和 mTLS 配置，
- 压缩，
- reporter 自监控指标，
- 以及对 broker 和客户端两者的支持。

该 reporter 也会发出它自己的健康指标，例如：

- `monedula_reporter_export_success_total`，
- `monedula_reporter_export_failure_total`，
- `monedula_reporter_export_duration_ms`。

因此，如果 collector 流水线出了问题，你不会只得到一片寂静。你会得到关于 reporter 本身导出失败的信号。

该项目还附带一个易于使用的快速上手示例，它用 Kafka、OpenTelemetry Collector、Prometheus 和 Grafana 演示了完整流程。而且，由于仪表盘本身就是问题的一部分，它还包含一组经过精心整理、可直接使用的 Grafana 仪表盘，与该插件产出的指标相匹配。

你可以在 GitHub 上找到它，在本地构建它，并用快速上手示例进行测试。预构建产物即将推出。

如果你发现了 bug 或有改进建议，欢迎在 GitHub 上创建一个 issue。

## 小结

Kafka 监控之所以比它本该有的样子更难，是因为 Kafka 通过两套不同的指标系统暴露指标，大多数生产环境仍然依赖 JMX，而每一个 JMX 到 Prometheus 的映射都会在 broker、Prometheus 和 Grafana 仪表盘之间制造出又一层兼容性。

`monedula-metrics-reporter` 采取了一条不同的路径：它作为一个 Kafka 指标 reporter 运行，原生地通过 OTLP 导出指标，处理两套 Kafka 指标 registry，并附带与所发出指标相匹配的仪表盘。

那么，Kafka 指标非得这么麻烦吗？

但愿，从此不再。