如今,Apache Kafka 是 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 监控文档指出,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 从 JMX 导出指标。它是一个以 Java agent 形式运行的库,通过一个规则文件和一个端口号进行配置。
该端口被用作 Prometheus 端点,后面附加 /metrics。
那规则文件呢?它是一个庞大的 YAML 文件,里面塞满了大量正则表达式。通常没人完全理解其中的细节,大家只是照用,寄希望于当初写它的人确实知道自己在做什么。
于是,你必须把 JMX Exporter 上传到所有 broker,可能还要创建或编辑带有规则的配置文件——祝你好运——然后重启 broker。如果一切顺利,你可以 curl 指标端点并看到指标。很好!
接着你把这些指标抓取到 Prometheus 中,在 Grafana 里下载一些 Kafka 仪表盘,然后……在大多数情况下,你看到的是「No Data」。为什么?因为规则没有任何统一的标准,所以唯一的选择就是编辑仪表盘,或者尝试再找一个。
但事情还没完:这两套指标系统中的每一个都可以用一个自定义的 class(即插件)来配置。看看上面的表格——是的,有两个设置项。一个带 kafka 前缀,另一个不带。一个是单数的 metric,另一个是复数的 metrics。
没错,就是这样。
嘿,都 2026 年了,Kafka 能用 OpenTelemetry 吗?
简短的回答是:不能直接用。但确实有一些办法可以让 Kafka 学会 OpenTelemetry 协议。
有几种做法:
- JMX Exporter 加上 OpenTelemetry Collector 中的 Prometheus receiver——与上文描述的 JMX 映射问题如出一辙。
- OpenTelemetry Collector JMX receiver——仍然基于 JMX,需要暴露 JMX,而且该组件现已被标记为弃用,并建议改用一个独立的 JMX Gatherer Java 程序。
- OpenTelemetry 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 与 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 是一个满足上述需求——并且不止于此——的开源库。
它使用 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 指标非得这么麻烦吗?
但愿,从此不再。