JVM 默认会通过 JMX 暴露基础指标,很多中间件也会通过 JMX 暴露业务指标,例如 Kafka、Zookeeper、ActiveMQ、Cassandra、Spark、Tomcat、Flink 等。掌握 JMX 监控方式,就掌握了一批 Java 程序的监控入口。本文介绍如何用 JMX Exporter 将 JMX 监控数据转换为 Prometheus 可识别的格式。
核心要点
- JMX 是 Java 程序暴露管理和监控能力的标准方式,指标通常以 MBean 形式存在。
- JMX Exporter 是一个 jar 包,可以作为
javaagent与业务 JVM 运行在同一个进程中。 - JMX Exporter 会读取 JMX 指标,并通过 HTTP 只读接口暴露 Prometheus 格式指标。
- Kafka、Zookeeper 等组件的 JMX 指标很多,不建议全部采集,应通过配置文件筛选需要的 MBean。
- 后续可以用 Prometheus、Categraf 的
input.prometheus插件或 vmagent 抓取 JMX Exporter 暴露的/metrics。
JMX 是什么
JMX(Java Management Extensions)是 Java 管理扩展的简称,是一种为 Java 应用程序植入管理能力的框架。Java 程序经常通过 JMX 暴露监控指标,也可以通过 JMX 控制程序行为,例如触发 GC。
在 Java 程序中,如果某个类实现了 MBean 接口,这个类就可以通过 JMX 读取和修改属性,也可以调用方法。JMX 通过 MBeanServer 管理 MBean。MBeanServer 是一个管理器,可以管理多个 MBean;每个 MBean 都有一个 ObjectName,用来唯一标识该 MBean。
更新信息可参考 JMX Specification。这不是本文重点。
JMX Exporter 是什么
JMX Exporter 是 Prometheus 社区提供的工具,用来将 JMX 暴露的监控指标转换为 Prometheus 可识别的格式。
JMX Exporter 本质上是一个 jar 包,常以 javaagent 方式运行,和业务 Java 或 Scala 程序运行在同一个 JVM 中。使用这种方式后,业务程序不需要再额外暴露 JMX 远程端口,JMX Exporter 会在本地读取 JMX 数据并通过 HTTP 只读方式暴露 Prometheus 指标,安全性也更容易控制。
安装 JMX Exporter
JMX Exporter 是 jar 包,安装方式很简单:下载到服务器即可。
下载地址:
https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/
一种常见目录规划是:
/opt/jmx-exporter:存放 JMX Exporter jar 包。/etc/jmx-exporter:存放不同组件的 JMX Exporter 配置文件。
以 Kafka 为例,它暴露的 JMX 指标很多,通常不需要全部采集。因此需要用配置文件告诉 JMX Exporter:哪些 MBean 需要采集,采集后如何命名为 Prometheus 指标。
JMX Exporter 提供了多个样例配置:
https://github.com/prometheus/jmx_exporter/tree/main/example_configs
使用 JMX 监控 Zookeeper
新版 Zookeeper 已经可以直接暴露 Prometheus 协议的监控数据,不一定需要通过 JMX 采集。另外,Zookeeper 也提供四字命令的监控数据采集方式,例如 Categraf 的 input.zookeeper 插件就采用这种方式。
本文为了演示 JMX 方式,仍以 JMX Exporter 采集 Zookeeper 为例。
1. 准备配置文件
从 JMX Exporter 的样例配置目录下载 Zookeeper 配置文件,放到:
/etc/jmx-exporter/zookeeper.yml
2. 配置 Zookeeper 启动参数
将 JMX Exporter jar 包作为 javaagent 加入 Zookeeper 启动参数。Zookeeper 可以通过 zookeeper-env.sh 控制启动参数:
# zookeeper-env.sh
SERVER_JVMFLAGS="-javaagent:/opt/jmx-exporter/jmx-exporter.jar=7070:/etc/jmx-exporter/zookeeper.yml"
上例将 JMX Exporter 的 HTTP 端口设置为 7070,配置文件为 /etc/jmx-exporter/zookeeper.yml。端口和路径可以按实际环境调整。
3. 重启并验证端口
重启 Zookeeper 后,先确认 7070 端口是否监听:
$ netstat -tlnp | grep 7070
tcp 0 0 0.0.0.0:7070 0.0.0.0:* LISTEN 892/java
再请求 /metrics 接口:
$ curl -s localhost:7070/metrics | head
# HELP jvm_threads_current Current thread count of a JVM
# TYPE jvm_threads_current gauge
jvm_threads_current 16.0
# HELP jvm_threads_daemon Daemon thread count of a JVM
# TYPE jvm_threads_daemon gauge
jvm_threads_daemon 12.0
# HELP jvm_threads_peak Peak thread count of a JVM
# TYPE jvm_threads_peak gauge
jvm_threads_peak 16.0
# HELP jvm_threads_started_total Started thread count of a JVM
看到 Prometheus 文本格式指标后,就可以用 Prometheus、Categraf 的 input.prometheus 插件或 vmagent 抓取。
使用 JMX 监控 Kafka
Kafka 的方式类似:准备 kafka.yml,再通过 KAFKA_OPTS 添加 JMX Exporter javaagent 参数。
$ export KAFKA_OPTS='-javaagent:/opt/jmx-exporter/jmx-exporter.jar=7071:/etc/jmx-exporter/kafka.yml'
$ /opt/kafka_2.11-0.10.1.0/bin/kafka-server-start.sh /opt/kafka_2.11-0.10.1.0/conf/server.properties
上例把端口设置为 7071,配置文件指定为 /etc/jmx-exporter/kafka.yml。
验证端口是否成功监听:
$ netstap -tlnp | grep 7071
tcp6 0 0 :::7071 :::* LISTEN 19288/java
$ curl -s localhost:7071 | grep -i kafka | head
# HELP kafka_server_replicafetchermanager_minfetchrate Attribute exposed for management (kafka.server<type=ReplicaFetcherManager, name=MinFetchRate, clientId=Replica><>Value)
# TYPE kafka_server_replicafetchermanager_minfetchrate untyped
kafka_server_replicafetchermanager_minfetchrate{clientId="Replica",} 0.0
# HELP kafka_network_requestmetrics_totaltimems Attribute exposed for management (kafka.network<type=RequestMetrics, name=TotalTimeMs, request=OffsetFetch><>Count)
# TYPE kafka_network_requestmetrics_totaltimems untyped
kafka_network_requestmetrics_totaltimems{request="OffsetFetch",} 0.0
kafka_network_requestmetrics_totaltimems{request="JoinGroup",} 0.0
kafka_network_requestmetrics_totaltimems{request="DescribeGroups",} 0.0
kafka_network_requestmetrics_totaltimems{request="LeaveGroup",} 0.0
kafka_network_requestmetrics_totaltimems{request="GroupCoordinator",} 0.0
如果 Kafka 通过 systemd 运行,可以在 kafka.service 中配置环境变量:
...
[Service]
Restart=on-failure
Environment=KAFKA_OPTS=-javaagent:/opt/jmx-exporter/jmx-exporter.jar=7071:/etc/jmx-exporter/kafka.yml
ExecStart=/opt/kafka/bin/kafka-server-start.sh /etc/kafka/server.properties
ExecStop=/opt/kafka/bin/kafka-server-stop.sh
TimeoutStopSec=600
User=kafka
...
JMX Exporter 配置文件如何理解
使用 JMX Exporter jar 包时,会引用一个配置文件。不同 Java 应用通过 JMX 暴露的指标数量可能非常多,采集哪些指标主要由配置文件决定。
下面是 /etc/jmx-exporter/zookeeper.yml 的部分内容:
rules:
# replicated Zookeeper
- pattern: "org.apache.ZooKeeperService<name0=ReplicatedServer_id(\\d+)><>(\\w+)"
name: "zookeeper_$2"
type: GAUGE
- pattern: "org.apache.ZooKeeperService<name0=ReplicatedServer_id(\\d+), name1=replica.(\\d+)><>(\\w+)"
name: "zookeeper_$3"
type: GAUGE
labels:
replicaId: "$2"
- pattern: "org.apache.ZooKeeperService<name0=ReplicatedServer_id(\\d+), name1=replica.(\\d+), name2=(\\w+)><>(Packets\\w+)"
name: "zookeeper_$4"
type: COUNTER
labels:
replicaId: "$2"
memberType: "$3"
- pattern: "org.apache.ZooKeeperService<name0=ReplicatedServer_id(\\d+), name1=replica.(\\d+), name2=(\\w+)><>(\\w+)"
name: "zookeeper_$4"
type: GAUGE
labels:
replicaId: "$2"
memberType: "$3"
- pattern: "org.apache.ZooKeeperService<name0=ReplicatedServer_id(\\d+), name1=replica.(\\d+), name2=(\\w+), name3=(\\w+)><>(\\w+)"
name: "zookeeper_$4_$5"
type: GAUGE
labels:
replicaId: "$2"
memberType: "$3"
# standalone Zookeeper
- pattern: "org.apache.ZooKeeperService<name0=StandaloneServer_port(\\d+)><>(\\w+)"
type: GAUGE
name: "zookeeper_$2"
- pattern: "org.apache.ZooKeeperService<name0=StandaloneServer_port(\\d+), name1=InMemoryDataTree><>(\\w+)"
type: GAUGE
name: "zookeeper_$2"
这个配置文件定义了一组 rules。每个 rule 通常由以下部分组成:
| 字段 | 作用 |
|---|---|
pattern |
用正则匹配 MBean 的 object name 和属性 |
name |
定义转换后的 Prometheus 指标名称 |
type |
指定指标类型,例如 GAUGE、COUNTER |
labels |
从正则匹配结果中提取标签值 |
JMX 监控采集的关键就在 rules:对某个 Java 应用,到底应该采集哪些 MBean、如何命名、哪些维度作为标签,决定了最终指标是否可用。
采集链路检查表
| 检查项 | 说明 |
|---|---|
| jar 路径正确 | -javaagent 中的 JMX Exporter jar 路径必须存在 |
| 配置文件正确 | Kafka、Zookeeper 等组件需要匹配自己的配置文件 |
| 端口未冲突 | 每个进程暴露的 JMX Exporter HTTP 端口不能冲突 |
/metrics 可访问 |
本机或采集器所在网络能访问指标端点 |
| 指标数量可控 | 不要无差别采集所有 MBean,避免指标过多 |
| 采集器配置完成 | Prometheus、Categraf 或 vmagent 需要配置抓取目标 |
FAQ
JMX Exporter 和远程 JMX 是什么关系?
JMX Exporter 可以在业务 JVM 内通过 javaagent 读取 JMX 数据,并通过 HTTP 暴露 Prometheus 指标。这样通常不需要额外开放远程 JMX 端口。
为什么不同组件需要不同配置文件?
不同 Java 组件暴露的 MBean 名称、属性和语义不同。配置文件中的 rules 决定哪些 MBean 被采集,以及如何映射为 Prometheus 指标。
已经有组件专用采集插件,还需要 JMX Exporter 吗?
不一定。像 Zookeeper 这类组件可能有 Prometheus 原生指标或四字命令采集方式。是否使用 JMX Exporter,要看现有暴露方式、采集插件成熟度和团队运维习惯。
小结
JMX Exporter 适合把 Java 中间件已有的 JMX 指标转换为 Prometheus 格式指标。落地时,关键步骤是下载 jar、准备组件配置文件、通过 javaagent 加入启动参数、验证 /metrics,再由 Prometheus、Categraf 或 vmagent 抓取。真正需要经验的是 rules 配置:采集哪些 MBean、如何命名、如何控制标签,决定了 JMX 监控是否长期可维护。
文章主要翻译了 https://alex.dzyoba.com/blog/jmx-exporter/ 并加入了一些个人理解。enjoy…make a better world :)
