JMX 是什么?
JMX 是 “Java Management Extensions” 的缩写,中文通常称为 “Java 管理扩展”。它是 Java 平台提供的一套标准框架,用于对 Java 应用程序、设备、系统资源进行监控和管理。
JMX 的核心作用包括:
很多 Java 应用(如 Kafka、Hadoop、Tomcat 等)都通过 JMX 暴露运行时指标,方便运维人员进行监控和故障排查。在监控领域,你就可以简单的理解 JMX 是一种 Java 应用暴露指标的标准方式。
很多新的开源项目直接暴露 Prometheus 协议的监控数据,确实更方便,但很多老的 Java 应用仍然通过 JMX 暴露指标数据,所以掌握 JMX 监控方式,等于掌握了一批 Java 应用的监控方式。
OpenTelemetry 的标准已经被业界普遍使用,JMX 也可以和 OpenTelemetry 集成。用于 JMX 的 OpenTelemetry 集成利用 YAML 中指定的配置文件来描述应用程序的指标应如何转换为 OpenTelemetry 指标。这些规则会提供给与 JMX API 交互的 JMX 指标抓取工具。许多现有的 Java 应用程序可配置为通过 JMX 发出指标。以下应用程序具有社区支持的预定义规则文件:
Activemq
Cassandra
Hbase
Hadoop
Jetty
Jvm
Kafka
Solr
Tomcat
Wildfly
这意味着,在使用 JMX 抓取工具时,可以将目标应用程序识别为上述选项之一,无需任何额外配置即可自动抓取指标。
使用 OpenTelemetry 的 JMX 集成从应用程序中提取数据有多种方法:
运行 JMX 指标收集器 JAR 并将其指向您的应用程序
将 OpenTelemetry Java 代理与应用程序一起运行
运行 OpenTelemetry Collector 并配置 JMX 接收器以通过 JMX 获取指标
JMX 指标收集器是一种简单便捷的方法,可用于测试从 Java 应用程序导出指标,且所需的额外依赖最少。OpenTelemetry Java 代理是一种更成熟且经过验证的机制,但由于它引入了额外的 instrumentation,可能也会带来一些开销。如果在你的环境中运行单独的进程或工作负载是可接受的,那么 OpenTelemetry 收集器是一个可靠的替代方案。本文将探讨这三种选择。
配置
在深入了解 OpenTelemetry 集成之前,有必要确保将要测试的应用程序正确暴露 JMX 端点。以下命令在 Docker 容器中启动 Tomcat 服务器,以减少本地系统所需的依赖项数量。它使用 JAVA_OPTS 环境变量启用 JMX,并使其在 9999 端口上可用。
$ docker run -it --rm -p 9999:9999 -p 8888:8080 -e JAVA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.rmi.port=9999 -Djava.rmi.server.hostname=0.0.0.0 -Dcom.sun.management.jmxremote.ssl=false" tomcat:9.0
复制代码
有许多工具可以验证 JMX 端口是否可访问以及是否在生成指标。以下使用 JConsole 并连接到已打开的端口:
$ jconsole localhost:9999
复制代码
这将弹出一个应用程序,它可能会提示您连接不安全。在本地开发时,此警告可以安全地忽略。
这些数据可通过远程 JMX 端口访问,我们已准备好与 OpenTelemetry 集成。
直接使用 JMX scraper
如前所述,JMX 抓取工具是最简便的入门方式。以下命令将:
$ wget https://repo1.maven.org/maven2/io/opentelemetry/contrib/opentelemetry-jmx-scraper/1.49.0-alpha/opentelemetry-jmx-scraper-1.49.0-alpha.jar
$ OTEL_METRICS_EXPORTER=console java -jar opentelemetry-jmx-scraper-1.49.0-alpha.jar -config - <<EOFotel.jmx.service.url=service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmiotel.jmx.target.system=tomcatEOF
复制代码
该命令的输出让我们知道连接已建立,并且一些度量工具已创建:
WARNING: A terminally deprecated method in sun.misc.Unsafe has been calledWARNING: sun.misc.Unsafe::objectFieldOffset has been called by io.opentelemetry.internal.shaded.jctools.util.UnsafeAccess (file:/Users/alex/Downloads/opentelemetry-jmx-scraper-1.49.0-alpha.jar)WARNING: Please consider reporting this to the maintainers of class io.opentelemetry.internal.shaded.jctools.util.UnsafeAccessWARNING: sun.misc.Unsafe::objectFieldOffset will be removed in a future release2025-08-27 10:28:48 INFO metrics export interval (seconds) = 602025-08-27 10:28:48 WARNING SASL unsupported in current environment: class io.opentelemetry.contrib.jmxscraper.JmxConnectorBuilder cannot access class com.sun.security.sasl.Provider (in module java.security.sasl) because module java.security.sasl does not export com.sun.security.sasl to unnamed module @24313fcc2025-08-27 10:28:48 INFO Connecting to service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi2025-08-27 10:28:48 INFO tomcat: found 3 metric rules2025-08-27 10:28:48 INFO JMX scraping started2025-08-27 10:28:49 INFO Created Counter for tomcat.error.count2025-08-27 10:28:49 INFO Created Counter for tomcat.request.count2025-08-27 10:28:49 INFO Created Gauge for tomcat.request.duration.max2025-08-27 10:28:49 INFO Created Counter for tomcat.request.duration.sum2025-08-27 10:28:49 INFO Created Counter for tomcat.network.io2025-08-27 10:28:49 INFO Created Counter for tomcat.network.io2025-08-27 10:28:49 INFO Created UpDownCounter for tomcat.thread.count2025-08-27 10:28:49 INFO Created UpDownCounter for tomcat.thread.limit2025-08-27 10:28:49 INFO Created UpDownCounter for tomcat.thread.busy.count
复制代码
使用 JMX 抓取工具是个不错的入门方法,但它确实需要一个额外的 JAR 包,如果我们已经在使用 Java 代理,可能就不希望这样了。
使用 OpenTelemetry Java 代理
除了提供 JMX 集成外,Java 代理还提供了自动检测应用程序的功能。出于这个原因,它比 JMX 抓取工具更常用。以下命令:
下载最新发布版本的 OpenTelemetry Java agent jar
通过将本地目录挂载到容器中,将 jar 包归档文件添加到 Docker 环境中
使用一些额外的环境变量运行与之前相同的 Tomcat 应用程序,以加载 agent jar 包
$ wget https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v2.19.0/opentelemetry-javaagent.jar
$ docker run -it -v `pwd`:/tmp --rm \ -e OTEL_LOGS_EXPORTER=console \ -e OTEL_TRACES_EXPORTER=console \ -e OTEL_METRICS_EXPORTER=console \ -e CATALINA_OPTS="$CATALINA_OPTS -javaagent:/tmp/opentelemetry-javaagent.jar" \ -e JAVA_OPTS="-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.local.only=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.port=9999 -Dcom.sun.management.jmxremote.rmi.port=9999 -Djava.rmi.server.hostname=0.0.0.0 -Dcom.sun.management.jmxremote.ssl=false -Dotel.jmx.target.system=tomcat" \ tomcat:9.0
复制代码
该命令的输出将与运行 JMX scraper 的输出非常相似:
04-Sep-2025 21:43:40.251 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [48] milliseconds[otel.javaagent 2025-09-04 21:43:40:787 +0000] [jmx_bean_finder] INFO io.opentelemetry.javaagent.shaded.instrumentation.jmx.engine.MetricRegistrar - Created Counter for tomcat.error.count[otel.javaagent 2025-09-04 21:43:40:787 +0000] [jmx_bean_finder] INFO io.opentelemetry.javaagent.shaded.instrumentation.jmx.engine.MetricRegistrar - Created Counter for tomcat.request.count[otel.javaagent 2025-09-04 21:43:40:789 +0000] [jmx_bean_finder] INFO io.opentelemetry.javaagent.shaded.instrumentation.jmx.engine.MetricRegistrar - Created Gauge for tomcat.request.duration.max[otel.javaagent 2025-09-04 21:43:40:789 +0000] [jmx_bean_finder] INFO io.opentelemetry.javaagent.shaded.instrumentation.jmx.engine.MetricRegistrar - Created Counter for tomcat.request.duration.sum[otel.javaagent 2025-09-04 21:43:40:789 +0000] [jmx_bean_finder] INFO io.opentelemetry.javaagent.shaded.instrumentation.jmx.engine.MetricRegistrar - Created Counter for tomcat.network.io[otel.javaagent 2025-09-04 21:43:40:789 +0000] [jmx_bean_finder] INFO io.opentelemetry.javaagent.shaded.instrumentation.jmx.engine.MetricRegistrar - Created Counter for tomcat.network.io[otel.javaagent 2025-09-04 21:43:40:790 +0000] [jmx_bean_finder] INFO io.opentelemetry.javaagent.shaded.instrumentation.jmx.engine.MetricRegistrar - Created UpDownCounter for tomcat.thread.count[otel.javaagent 2025-09-04 21:43:40:790 +0000] [jmx_bean_finder] INFO io.opentelemetry.javaagent.shaded.instrumentation.jmx.engine.MetricRegistrar - Created UpDownCounter for tomcat.thread.limit[otel.javaagent 2025-09-04 21:43:40:791 +0000] [jmx_bean_finder] INFO io.opentelemetry.javaagent.shaded.instrumentation.jmx.engine.MetricRegistrar - Created UpDownCounter for tomcat.thread.busy.count
复制代码
如果我们的部署已经在使用 OpenTelemetry Java 代理,现在我们就有了一种无需引入额外工具即可收集 JMX 指标的方法。但如果我们更倾向于使用外部工具,那该怎么办呢?别担心,OpenTelemetry 这把“瑞士军刀”已经为你准备好了!
使用 OpenTelemetry Collector 和 JMX receiver
我们将要探讨的最后一种将 JMX 指标转换为 OpenTelemetry 的方法是使用 OpenTelemetry Collector。Collector 提供了许多集成功能,通常作为遥测管道的一部分部署在基础设施内部。Collector 由许多组件构成,其中之一是 JMX receiver,它在底层使用 JMX scraper 与 Java 应用程序进行交互。
在本示例中,我们将继续使用之前一直在用的 tomcat 应用程序,并使用由社区发布和维护的 OpenTelemetry Collector Contrib 发行版。运行带有调试导出器的 Collector 需要以下配置文件,该调试导出器会将指标输出到控制台。由于 Collector 便于配置多个导出器,我们还添加了一个 OpenTelemetry 协议(OTLP)导出器,用于将数据发送到 Honeycomb(译者注:一个可观测性云厂商):
receivers: jmx/tomcat: jar_path: /tmp/opentelemetry-jmx-scraper-1.48.0-alpha.jar endpoint: localhost:9999 target_system: tomcat collection_interval: 10s
exporters: otlphttp: endpoint: https://api.honeycomb.io:443 headers: x-honeycomb-team: ${env:HONEYCOMB_API_KEY} x-honeycomb-dataset: jmx-test debug: verbosity: detailed
service: telemetry: logs: level: debug pipelines: metrics: receivers: [jmx/tomcat] exporters: [debug, otlphttp]
复制代码
请注意,要使此配置生效,我们需要指定 JMX 抓取器 JAR 的位置,因为接收器本身并不包含该 JAR。有了这份配置,以下命令将下载最新发布的 Collector Contrib 发行版并运行它:
$ wget https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.133.0/otelcol-contrib_0.133.0_darwin_arm64.tar.gz -O - | tar -xz$ ./otelcol-contrib --config configuration.yaml
复制代码
收集器的输出将包含来自 JMX 抓取器的详细信息以及来自调试导出器的日志,以验证数据是否按照配置进行导出。
使用 Collector 使我们能够收集指标,而无需修改应用程序或应用程序运行的环境,只要该应用程序远程暴露 JMX 端口即可。 Collector 还增加了一些灵活性,可以根据需要对数据进行转换或丰富。
自定义应用的监控采集
OpenTelemetry 社区支持的应用程序列表不足以处理所有现有的 Java 应用程序。幸运的是,可以定义自定义规则配置文件来支持自定义应用程序。该项目的官方文档更详细地描述了编写自定义配置的语法。在下面的示例中,我们创建的规则为 Resin 服务器暴露的指标提供了映射。此配置将 resin 命名空间中 ThreadPool 托管 Bean(MBean)的 JMX 属性 ThreadActiveCount 映射到 OpenTelemetry 中名为 resin.threadpool.active.count 的 gauge(量规):
rules: - bean: resin:type=ThreadPool type: gauge unit: "{thread}" mapping: ThreadActiveCount: metric: resin.threadpool.active.count desc: "Current number of active threads in the pool"
复制代码
将 OpenTelemetry 与 JMX 集成的每种方法都支持自定义配置文件。JMX 抓取器通过 otel.jmx.config 属性来支持这一点。收集器中的 JMX 接收器可以使用 jmx_configs 而非 target_system 进行配置。允许收集器使用此自定义文件的配置如下:
receivers: jmx/resin: jar_path: /Users/alex/Downloads/opentelemetry-jmx-scraper-1.48.0-alpha.jar endpoint: localhost:9999 jmx_configs: /tmp/resin.rules.yaml collection_interval: 10s
复制代码
总结
有了这些,我们现在就具备了将生成 JMX 遥测数据的应用程序集成到任何支持 OpenTelemetry 的遥测管道中的工具。可以灵活选择使用 JMX 抓取器、Java 代理或收集器,这确保我们能够选用适合自身环境的工具,同时对现有工作流程的影响降至最低。
评论