把握行业变革关键节点,12 月 19 日 - 20 日,AICon北京站即将重磅启幕! 了解详情
写点什么

Java JVM 可观测的原理解释和落地方案对比

作者:谭建

  • 2023-03-23
    北京
  • 本文字数:12818 字

    阅读完需:约 42 分钟

Java JVM 可观测的原理解释和落地方案对比

如何监控 JVM


云原生时代,我们使用 Prometheus 来作为可观测平台的指标监控的核心组件。监控一个云原生的 Java 应用,一般会分成 3 个步骤:


  1. 配置 Java 应用,暴露 JVM 指标信息

  2. 通过 Prometheus 采集并存储指标数据

  3. 通过 Grafana 的 Dashboard 展示采集到的指标


借力社区生态,我们有越来越丰富的方式来暴露 JVM 核心指标,文本将介绍 JVM 监控的基本原理,并重点介绍常见的 3 种暴露 JVM 指标的方案,可以根据读者的使用场景来选择。


采集原理


Java Management Extensions(JMX)技术是 Java SE 平台的标准功能,提供了一种简单的、标准的监控和管理资源的方式,对于如何定义一个资源给出了明确的结构和设计模式,主要用于监控和管理 Java 应用程序运行状态、设备和资源信息、Java 虚拟机运行情况等信息。并且如下图所示,有关应用程序性能和资源使用情况的详细信息可以从 JMX 指标中导出。如果有任何问题,我们可以借助收集的指标进行诊断,并对系统进行微调以获得最佳性能。JMX 是可以动态的,所以也可以在资源创建、安装、实现时进行动态监控和管理,JDK 自带的 jconsole 就是使用 JMX 技术实现的监控工具。图片:



使用 JMX 技术时,通过定义一个被称为 MBean 或 MXBean 的 Java 对象来表示要管理指定的资源,然后可以把资源信息注册到 MBean Server 对外提供服务。MBean Server 充当了对外提供服务和对内管理 MBean 资源的代理功能,如此优雅的设计让 MBean 资源管理和 MBean Server 代理完全独立开,使之可以自由的控制 MBean 资源信息。


JMX 不仅仅用于本地管理,JMX Remote API 为 JMX 添加了远程功能,使之可以通过网络远程监视和管理应用程序。得益于相对独立的架构设计,使 JMX 可以平滑的集成到各种监控系统之中。并具有包括遵守 Java 规范、通用的管理方式、开箱即用的监控功能、架构设计优秀、监测 JVM 状态能力、管理解决方案集成的能力等优点。


JMX 技术架构主要有资源管理(MBean/MXBean)模块,资源代理模块(MBean Server),远程管理模块(Remote API)组成 ,下面的图片来自维基百科,很好的展示了三个模块之间的关系。图片:



资源管理在架构中标识为资源探测层(Probe Level),在 JMX 中, 使用 MBean 或 MXBean 来表示一个资源(下面简称 MBean),访问和管理资源也都是通过 MBean。


JMX 已经对 JVM 进行了多维度资源检测,所以可以轻松启动 JMX 代理来访问内置的 JVM 资源检测,从而通过 JMX 技术远程监控和管理 JVM。JMX 内置了常用的 JVM 资源监测类,包括但不限于:类加载、垃圾收集、日志系统、内存池、内存、内存系统、操作系统、运行时系统和线程系统。


以下代码演示了如何使用原生 JMX 接口获取 JVM 指标:

package org.example.jmx;
import java.lang.management.CompilationMXBean;import java.lang.management.GarbageCollectorMXBean;import java.lang.management.ManagementFactory;import java.lang.management.MemoryMXBean;import java.lang.management.MemoryManagerMXBean;import java.lang.management.MemoryUsage;import java.lang.management.OperatingSystemMXBean;import java.util.Arrays;import java.util.List;import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) { printOSInfo(); System.out.println("================"); printMemoryManagerInfo(); System.out.println("================"); printGCInfo(); }
//通过 OperatingSystemMXBean 获取 OS 信息 public static void printOSInfo() { OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); String osName = operatingSystemMXBean.getName(); String osVersion = operatingSystemMXBean.getVersion(); int processors = operatingSystemMXBean.getAvailableProcessors(); System.out.println(String.format("OS:%s,Version:%s,CPU:%d 个", osName, osVersion, processors));
CompilationMXBean compilationMXBean = ManagementFactory.getCompilationMXBean(); String compilationMXBeanName = compilationMXBean.getName(); System.out.println("编译系统:" + compilationMXBeanName);
MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); MemoryUsage heapMemoryUsage = memoryMXBean.getHeapMemoryUsage(); long max = heapMemoryUsage.getMax(); long used = heapMemoryUsage.getUsed(); System.out.println(String.format("使用内存:%dMB/%dMB", used / 1024 / 1024, max / 1024 / 1024));
List<GarbageCollectorMXBean> gcMXBeans = ManagementFactory.getGarbageCollectorMXBeans(); String gcNames = gcMXBeans.stream() .map(MemoryManagerMXBean::getName) .collect(Collectors.joining(",")); System.out.println("垃圾收集器:" + gcNames); }
// 通过 MemoryManagerMXBean 获取 JVM 内存管理器信息 public static void printMemoryManagerInfo() { List<MemoryManagerMXBean> managers = ManagementFactory.getMemoryManagerMXBeans(); if (managers != null && !managers.isEmpty()) { for (MemoryManagerMXBean manager : managers) { System.out.println("vm内存管理器:名称=" + manager.getName() + ",管理的内存区=" + Arrays.deepToString( manager.getMemoryPoolNames()) + ",ObjectName=" + manager.getObjectName()); } } }
//通过 GarbageCollectorMXBean 获取 JVM GC 信息 public static void printGCInfo() { try { List<GarbageCollectorMXBean> gcMxBeans = ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean gcMxBean : gcMxBeans) { System.out.println(gcMxBean.getName()); System.out.println(gcMxBean.getObjectName()); }
} catch (RuntimeException re) { throw re; } catch (Exception exp) { throw new RuntimeException(exp); } }}
复制代码


以上代码运行结果如下:


OS:Mac OS X,Version:10.16,CPU:8 个编译系统:HotSpot 64-Bit Tiered Compilers使用内存:4MB/4096MB垃圾收集器:G1 Young Generation,G1 Old Generation================vm内存管理器:名称=CodeCacheManager,管理的内存区=[CodeHeap 'non-nmethods', CodeHeap 'profiled nmethods', CodeHeap 'non-profiled nmethods'],ObjectName=java.lang:type=MemoryManager,name=CodeCacheManagervm内存管理器:名称=Metaspace Manager,管理的内存区=[Metaspace, Compressed Class Space],ObjectName=java.lang:type=MemoryManager,name=Metaspace Managervm内存管理器:名称=G1 Young Generation,管理的内存区=[G1 Eden Space, G1 Survivor Space, G1 Old Gen],ObjectName=java.lang:type=GarbageCollector,name=G1 Young Generationvm内存管理器:名称=G1 Old Generation,管理的内存区=[G1 Eden Space, G1 Survivor Space, G1 Old Gen],ObjectName=java.lang:type=GarbageCollector,name=G1 Old Generation================G1 Young Generationjava.lang:type=GarbageCollector,name=G1 Young GenerationG1 Old Generationjava.lang:type=GarbageCollector,name=G1 Old Generation
复制代码


常见开源暴露 JVM 指标工具


通过原生 JMX 获取 JVM 指标,并能够被我们的可观测性系统采集到工作量还是很大。以下,列举了常见的几款开箱即用的工具:


Micrometer/Spring Boot Actuator


Spring Boot Actuator 模块提供了生产级别的功能,比如健康检查,审计,指标收集,HTTP 跟踪等,帮助我们监控和管理 Spring Boot 应用。这个模块是一个采集应用内部信息暴露给外部的模块,上述的功能都可以通过 HTTP 和 JMX 访问。Spring Boot Actuator 使用 Micrometer 与这些外部应用程序监视系统集成,Spring Boot 应用只需很少的配置即可轻松集成外部的监控系统。


以下是 Micrometer 官方介绍:


Micrometer provides a simple facade over the instrumentation clients for the most popular observability systems, allowing you to instrument your JVM-based application code without vendor lock-in. Think SLF4J, but for application observability! Data recorded by Micrometer are intended to be used to observe, alert, and react to the current/recent operational state of your environment.


在 pom.xml 中添加如下依赖:

<dependency>   <groupId>org.springframework.boot</groupId>   <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency>   <groupId>io.micrometer</groupId>   <artifactId>micrometer-registry-prometheus</artifactId>   <scope>runtime</scope> </dependency>
复制代码


  • 在 src/main/resources/application.properties 中添加相关配置


management.endpoints.web.exposure.include=*management.endpoints.web.exposure.include=prometheus,health,info,metricmanagement.endpoints.web.base-path=/
management.server.port=8999management.health.probes.enabled=truemanagement.endpoint.health.show-details=alwaysmanagement.endpoint.prometheus.enabled=true
复制代码


  • 重新构建并运行服务


运行服务之后,我们可以通过 http://lcoalhost:8999/prometheus 访问服务暴露出来的 OpenMetrics 格式的指标。同时,该接口也能被 prometheus 抓取并收集指标, prometheus.yaml 配置文件参考如下:


prometheus: config:   scrape_configs:     - job_name: "jvm-metrics"       scrape_interval: 10s       metrics_path: "/prometheus"       static_configs:         - targets: ["<IP of the demo app:8999>"]
复制代码

Prometheus JMX Exporter


JMX-Exporter 主要通过连接到 Java 原生指标收集系统 JMX,并将指标转换为 Prometheus 可以理解的格式。JMX-Exporter 提供了两种采集并暴露指标的用法:


  1. 启动独立进程。JVM 启动时指定参数,暴露 JMX 的 RMI 接口,JMX-Exporter 调用 RMI 获取 JVM 运行时状态数据,转换为 Prometheus metrics 格式,并暴露端口让 Prometheus 采集

  2. JVM 进程内启动(in-process)。JVM 启动时指定参数,通过 javaagent 的形式运行 JMX-Exporter 的 jar 包,进程内读取 JVM 运行时状态数据,转换为 Prometheus metrics 格式,并暴露端口让 Prometheus 采集。


官方不推荐使用第一种方式,一方面配置复杂,另一方面因为它需要一个单独的进程,而这个进程本身的监控又成了新的问题,所以本文重点围绕第二种用法以及如何在 Kubernetes 环境下使用 JMX Exporter 暴露 JVM 监控指标。


由于在应用启动时要通过 javaagent 指定 jmx exporter Jar 包,因此,我们需要考虑启动时如何让 JVM 正确的找到 jmx exporter Jar 包。以下列举了两种方式:


  1. 在构建应用镜像时将 JAR 文件打进镜像


根据 JMX EXporter 官方介绍,需要为其指定配置文件,需要提前创建好 prometheus-jmx-config.yaml , 更多配置项请参考官方文档, 以下内容仅供参考:


lowercaseOutputLabelNames: truelowercaseOutputName: truewhitelistObjectNames: ["java.lang:type=OperatingSystem"]blacklistObjectNames: []rules:  - pattern: 'java.lang<type=OperatingSystem><>(committed_virtual_memory|free_physical_memory|free_swap_space|total_physical_memory|total_swap_space)_size:'    name: os_$1_bytes    type: GAUGE    attrNameSnakeCase: true  - pattern: 'java.lang<type=OperatingSystem><>((?!process_cpu_time)\w+):'    name: os_$1    type: GAUGE    attrNameSnakeCase: true
复制代码


通过在应用 Dockerfile 中将 JAR 拷贝进镜像或者在线下载 JAR 文件实现,以下 Dockerfile 仅供参考:


FROM openjdk:11.0.15-jre
WORKDIR /app/
COPY target/my-app.jar ./# 将前面创建的 config 文件拷贝至镜像COPY prometheus-jmx-config.yaml ./
# 在线下载 jmx prometheus javaagent jarRUN set -ex; \ curl -L -O https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/0.17.2/jmx_prometheus_javaagent-0.17.2.jar;
# JAVA_TOOL_OPTIONS 会被 JVM 调用ENV JAVA_TOOL_OPTIONS=-javaagent:/app/jmx_prometheus_javaagent-0.17.2.jar=8088:/app/prometheus-jmx-config.yaml
EXPOSE 8081 8999 8080 8888
ENTRYPOINT java $JAVA_OPTS -jar my-app.jar
复制代码


b. 通过 Kubernetes Init Container 在应用启动之前将 JAR 文件挂载进容器


我们需要先将 JMX exporter 做成 Docker 镜像, 以下 Dockerfile 仅供参考:


FROM alpine/curl:3.14
WORKDIR /app/# 将前面创建的 config 文件拷贝至镜像COPY prometheus-jmx-config.yaml ./
# 在线下载 jmx prometheus javaagent jarRUN set -ex; \ curl -L -O https://repo1.maven.org/maven2/io/prometheus/jmx/jmx_prometheus_javaagent/0.17.2/jmx_prometheus_javaagent-0.17.2.jar;
复制代码


根据上面 Dockerfile 构建镜像:docker build -t my-jmx-exporter .


在 kubernetes 编排文件中使用该镜像,并将 JAR 通过共享目录挂载进容器:

apiVersion: apps/v1kind: Deploymentmetadata:  name: my-demo-app  labels:    app: my-demo-appspec:  selector:    matchLabels:      app: my-demo-app  template:    metadata:      labels:        app: my-demo-app    spec:      imagePullSecrets:      - name: registry-pull      initContainers:      - name: jmx-sidecar        image: my-jmx-exporter        command: ["cp", "-r", "/app/jmx_prometheus_javaagent-0.17.2.jar", "/target/jmx_prometheus_javaagent-0.17.2.jar"]  ➊        volumeMounts:        - name: sidecar          mountPath: /target      containers:      - image: my-demo-app-image        name: my-demo-app        resources:          requests:            memory: "1000Mi"            cpu: "500m"          limits:            memory: "1000Mi"            cpu: "500m"        ports:        - containerPort: 18083        env:        - name: JAVA_TOOL_OPTIONS          value: "-javaagent:/app/jmx_prometheus_javaagent-0.17.2.jar=8088:/app/prometheus-jmx-config.yaml" ➋        volumeMounts:        - name: host-time          mountPath: /etc/localtime          readOnly: true        - name: sidecar          mountPath: /sidecar      volumes:      - name: host-time        hostPath:          path: /etc/localtime      - name: sidecar  #共享agent文件夹        emptyDir: {}      restartPolicy: Always
复制代码


经过如上的改造之后, my-demo-app 具备了暴露 JVM 指标的能力,运行服务之后,我们可以通过 http://lcoalhost:8088 访问服务暴露出来的 prometheus 格式的指标。


同时,该接口也能被 prometheus 抓取并收集指标, prometheus.yaml 配置文件参考如下:

prometheus: config:   scrape_configs:     - job_name: "jvm-metrics"       scrape_interval: 10s       metrics_path: "/"       static_configs:         - targets: ["IP of the demo app:8088>"]
复制代码


OpenTelemetry Java Agent


在 Opentelemetry Agent v1.20.0 及以上版本中,Opentelemetry Agent 新增了 JMX Metric Insight 模块,如果你的应用已经集成了 Opentelemetry Agent 去采集应用链路,那么你不再需要另外引入其他 Agent 去为我们的应用暴露 JMX 指标。Opentelemetry Agent 也是通过检测应用程序中本地可用的 MBean 公开的指标,对其进行收集并暴露指标。


Opentelemetry Agent 也针对常见的 Java Server 或框架内置了一些监控的样例,请参考 预定义的指标。


使用 OpenTelemetry Java Agent 同样需要考虑如何将 JAR 挂载进容器,除了可以参考上面 JMX Exporter 挂载 JAR 文件的方式外,我们还可以借助 Opentelemetry 提供的 Operator 的能力来实现自动为我们的应用开启 JVM 指标暴露:


  • 在 Kubernetes 中安装 Opentelemetry Operator

kubectl apply -f https://github.com/open-telemetry/opentelemetry-operator/releases/latest/download/opentelemetry-operator.yaml
复制代码


  • 安装 Instrumentations 资源


一旦上面的 operator 安装就绪之后,我们就可以使用其内置的 Instrumentations 资源,如下文件告诉了 Operator 后续为我们应用注入 sidecar 的时候的一些差异化行为,比如文件中针对 Java 应用定义了两个环境变量,该环境变量是将 Opentelemetry Java Agent 的指标以 Prometheus 导出器将指标通过 9464 端口暴露出来。

kubectl apply -f - <<EOFapiVersion: opentelemetry.io/v1alpha1kind: Instrumentationmetadata:  name: opentelemetry-autoinstrumentation  namespace: defaultspec:  # https://github.com/open-telemetry/opentelemetry-operator/blob/main/docs/api.md#instrumentationspecresource  resource:    addK8sUIDAttributes: true  java:    env:			- name: OTEL_METRICS_EXPORTER        value: "prometheus"      - name: OTEL_METRICS_EXPORTER_PORT        value: "9464"EOF
复制代码


  • 为应用注入 sidecar


上面我们创建了一个 Instrumentation 资源,只是申明了 Operator 注入 sidecar 的时候的模版,我们还得需要告诉 Operator 哪些应用需要注入这个 sidecar。假设需要给名字为 my-demo-app 的 Deployment 注入 sidecar,需要在 spec.annotations 下添加 instrumentation.opentelemetry.io/inject-java: "default/opentelemetry-autoinstrumentation"注解:

apiVersion: apps/v1kind: Deploymentmetadata:  name: my-demo-app  labels:    app: my-demo-appspec:  selector:    matchLabels:      app: my-demo-app  replicas: 1  template:    metadata:      labels:        app: my-demo-app      annotations:				# 在 spec.annotations 下添加该注解        instrumentation.opentelemetry.io/inject-java: "default/opentelemetry-autoinstrumentation"    spec:      containers:      - name: my-demo-app        image: my-jmx-exporter        ports:          - containerPort: 8080            protocol: TCP
复制代码


最终生成的 YAML 内容如下:

apiVersion: v1kind: Podmetadata:  name: my-demo-app-565bd877dd-nqkk6  generateName: my-demo-app-565bd877dd-  namespace: default  uid: aa89ca0d-620c-4d20-8bc1-37d67bad4ea4  resourceVersion: '2668986'  creationTimestamp: '2022-04-08T05:58:48Z'  labels:    app: my-demo-app    pod-template-hash: 565bd877dd  annotations:    instrumentation.opentelemetry.io/inject-java: 'true'    sidecar.opentelemetry.io/inject: 'false'spec:  volumes:    - name: opentelemetry-auto-instrumentation      emptyDir: {}  initContainers:    - name: opentelemetry-auto-instrumentation      image: >-        ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-java      command:        - cp        - /javaagent.jar        - /otel-auto-instrumentation/javaagent.jar      volumeMounts:        - name: opentelemetry-auto-instrumentation          mountPath: /otel-auto-instrumentation  containers:    - name: my-demo-app      image: my-jmx-exporter      env:				- name: OTEL_METRICS_EXPORTER	        value: "prometheus"	      - name: OTEL_METRICS_EXPORTER_PORT	        value: "9464"        - name: JAVA_TOOL_OPTIONS          value: ' -javaagent:/otel-auto-instrumentation/javaagent.jar'        - name: OTEL_TRACES_EXPORTER          value: otlp        - name: OTEL_EXPORTER_OTLP_ENDPOINT          value: http://opentelemetry-collector.svc.cluster.local:4317        - name: OTEL_SERVICE_NAME          value: my-demo-app        - name: OTEL_RESOURCE_ATTRIBUTES_POD_NAME          valueFrom:            fieldRef:              apiVersion: v1              fieldPath: metadata.name        - name: OTEL_RESOURCE_ATTRIBUTES_POD_UID          valueFrom:            fieldRef:              apiVersion: v1              fieldPath: metadata.uid        - name: OTEL_RESOURCE_ATTRIBUTES_NODE_NAME          valueFrom:            fieldRef:              apiVersion: v1              fieldPath: spec.nodeName        - name: OTEL_RESOURCE_ATTRIBUTES          value: >-            k8s.container.name=my-demo-app,k8s.deployment.name=my-demo-app,k8s.deployment.uid=8de6929d-dda0-436c-bca1-604e9ca7ea4e,k8s.namespace.name=default,k8s.node.name=$(OTEL_RESOURCE_ATTRIBUTES_NODE_NAME),k8s.pod.name=$(OTEL_RESOURCE_ATTRIBUTES_POD_NAME),k8s.pod.uid=$(OTEL_RESOURCE_ATTRIBUTES_POD_UID),k8s.replicaset.name=my-deployment-with-sidecar-565bd877dd,k8s.replicaset.uid=190d5f6e-ba7f-4794-b2e6-390b5879a6c4        - name: OTEL_PROPAGATORS          value: jaeger,b3      volumeMounts:        - name: opentelemetry-auto-instrumentation          mountPath: /otel-auto-instrumentation      terminationMessagePath: /dev/termination-log      terminationMessagePolicy: File      imagePullPolicy: Always  restartPolicy: Always  securityContext:    runAsUser: 1000    runAsGroup: 3000    fsGroup: 2000
复制代码


经过如上的改造之后, my-demo-app 具备了暴露 JVM 指标的能力,运行服务之后,我们可以通过 http://lcoalhost:9464 访问服务暴露出来的 prometheus 格式的指标。同时,该接口也能被 prometheus 抓取并收集指标, prometheus.yaml 配置文件参考如下:

prometheus: config:   scrape_configs:     - job_name: "otel-jvm-metrics"       scrape_interval: 10s       metrics_path: "/"       static_configs:         - targets: ["<IP of the demo app"]
复制代码


使用 Prometheus 和 Grafana 统一 监控 JVM


前面介绍了几种开源暴露 JVM 指标的工具,通过上面几款工具,可以使我们的 Java 应用具备 JVM 暴露能力,接下来我们就需要考虑如何将这些指标集中采集存储并展示分析。本文主要介绍 Prometheus 和 Grafana 的方案并以使用 Opentelemetry Java Agent 作为 JVM 指标暴露为例:


  • 查看 Java 应用运行日志,加载 OTEL Agent:



  • 以下 Prometheus Scrape 抓取指标配置文件,与前面 Opentelemetry Java Agent 章节末尾提供的 prometheus.yaml 配置文件一致 :

prometheus: config:   scrape_configs:     - job_name: "otel-jvm-metrics"       scrape_interval: 10s       metrics_path: "/"       static_configs:         - targets: ["<IP of the demo app:9464>"]
复制代码


以下是 OTEL Java Agent 通过 JMX 采集并暴露出来的部分指标:

# TYPE target info# HELP target Target metadatatarget_info{container_id="3da5e8bc10cf207f01e0373a82c7bb3f7b8c430a757b6412f2199967a7971f9a",host_arch="amd64",host_name="3da5e8bc10cf",os_description="Linux 5.10.104-linuxkit",os_type="linux",process_command_line="/usr/local/openjdk-11/bin/java -javaagent:/app/opentelemetry-javaagent.jar -Dspring.cloud.nacos.config.enabled=false -Dspring.randomError=false -Dotel.jmx.config=/app/jmx_config_file.yaml -Dotel.metrics.exporter=prometheus -Dotel.exporter.prometheus.port=9464",process_executable_path="/usr/local/openjdk-11/bin/java",process_pid="7",process_runtime_description="Oracle Corporation OpenJDK 64-Bit Server VM 11.0.15+10",process_runtime_name="OpenJDK Runtime Environment",process_runtime_version="11.0.15+10",service_name="adservice-springcloud",telemetry_auto_version="1.22.1",telemetry_sdk_language="java",telemetry_sdk_name="opentelemetry",telemetry_sdk_version="1.22.0"} 1# TYPE otel_scope_info info# HELP otel_scope_info Scope metadataotel_scope_info{otel_scope_name="io.opentelemetry.micrometer-1.5"} 1# TYPE otel_scope_info info# HELP otel_scope_info Scope metadataotel_scope_info{otel_scope_name="io.opentelemetry.runtime-metrics",otel_scope_version="1.22.1-alpha"} 1# TYPE executor_pool_core gauge# HELP executor_pool_core The core number of threads for the poolexecutor_pool_core{otel_scope_name="io.opentelemetry.micrometer-1.5",name="applicationTaskExecutor"} 8.0 1676368076291# TYPE logback_events_total counter# HELP logback_events_total Number of events that made it to the logslogback_events_total{otel_scope_name="io.opentelemetry.micrometer-1.5",level="info"} 12.0 1676368076291logback_events_total{otel_scope_name="io.opentelemetry.micrometer-1.5",level="warn"} 3.0 1676368076291# TYPE jvm_gc_live_data_size gauge# HELP jvm_gc_live_data_size Size of long-lived heap memory pool after reclamationjvm_gc_live_data_size{otel_scope_name="io.opentelemetry.micrometer-1.5"} 2.7941376E7 1676368076291# TYPE executor_pool_max gauge# HELP executor_pool_max The maximum allowed number of threads in the poolexecutor_pool_max{otel_scope_name="io.opentelemetry.micrometer-1.5",name="applicationTaskExecutor"} 2.147483647E9 1676368076291# TYPE disk_total gauge# HELP disk_total Total space for pathdisk_total{otel_scope_name="io.opentelemetry.micrometer-1.5",path="/app/."} 4.2006183936E10 1676368076291# TYPE executor_active gauge# HELP executor_active The approximate number of threads that are actively executing tasksexecutor_active{otel_scope_name="io.opentelemetry.micrometer-1.5",name="applicationTaskExecutor"} 0.0 1676368076291# TYPE disk_free gauge# HELP disk_free Usable space for pathdisk_free{otel_scope_name="io.opentelemetry.micrometer-1.5",path="/app/."} 1.8299908096E10 1676368076291# TYPE system_cpu_count gauge# HELP system_cpu_count The number of processors available to the Java virtual machinesystem_cpu_count{otel_scope_name="io.opentelemetry.micrometer-1.5"} 2.0 1676368076291# TYPE jvm_memory_committed gauge# HELP jvm_memory_committed The amount of memory in bytes that is committed for the Java virtual machine to usejvm_memory_committed{otel_scope_name="io.opentelemetry.micrometer-1.5",area="heap",id="G1 Old Gen"} 6.6060288E7 1676368076291jvm_memory_committed{otel_scope_name="io.opentelemetry.micrometer-1.5",area="nonheap",id="Compressed Class Space"} 9175040.0 1676368076291jvm_memory_committed{otel_scope_name="io.opentelemetry.micrometer-1.5",area="nonheap",id="CodeHeap 'non-profiled nmethods'"} 4653056.0 1676368076291jvm_memory_committed{otel_scope_name="io.opentelemetry.micrometer-1.5",area="nonheap",id="CodeHeap 'profiled nmethods'"} 1.8087936E7 1676368076291jvm_memory_committed{otel_scope_name="io.opentelemetry.micrometer-1.5",area="nonheap",id="Metaspace"} 6.422528E7 1676368076291jvm_memory_committed{otel_scope_name="io.opentelemetry.micrometer-1.5",area="heap",id="G1 Survivor Space"} 7340032.0 1676368076291jvm_memory_committed{otel_scope_name="io.opentelemetry.micrometer-1.5",area="heap",id="G1 Eden Space"} 1.00663296E8 1676368076291jvm_memory_committed{otel_scope_name="io.opentelemetry.micrometer-1.5",area="nonheap",id="CodeHeap 'non-nmethods'"} 2555904.0 1676368076291......
复制代码


  • 在 Grafana 中导入 Opentelemetry JVM Metrics DashBoard( ID=8563 )


DashBoard 根据 Prometheus 采集到的指标,绘制出了我们常用的一些指标面板,比如 CPU 和内存负载情况,线程运行状况等等,也可以基于该 DashBoard 进行自定义。图片: h






  • 该部分的 Demo 文件可以参考底部参考部分的 jmx-jvm-demo


总结:


本文介绍了 JVM 监控的技术原理,同时介绍了常见的几种开源暴露 JVM 指标的工具。从接入方式来看主要分为两类,第一类是像 Spring Boot Actuator 需要客户应用做一些改造的方式,另一类是 JMX Exporter 和 Opentelemetry Java Agent 无侵入的方式。


以下针对这三种方案进行了多维度比较:



可以根据业务自身情况选择使用哪一种方式,针对运行在非容器平台的 Spring Boot 应用,建议选择 Spring Boot Actuator 方式,该方式能够更好的和 Spring Boot 生态集成。


如果业务应用运行在容器平台,建议选择 Opentelemetry Java Agent 的方式,该方式支持 Operator 的方式,能够以最小代价对 Java 应用进行监控,同时也方便后续对 Agent 版本的升级与运维。如果应用目前已经通过 Opentelemetry Java Agent 实现了链路采集,那么更加建议使用 Operator 云原生的方式。


参考:











作者介绍


谭建,「DaoCloud 」可观测性技术专家。负责研发新一代云原生操作系统底座,DCE 5.0 社区版 https://docs.daocloud.io/download/dce5/


  • 欢迎大家关注“OpenTelemetry” 公众号,这是中国区唯一官方技术公众号

  • 想加入技术社群和参与活动的朋友联系我

2023-03-23 09:1214545

评论

发布
暂无评论
发现更多内容

闲鱼商品列表API秘籍!轻松获取列表数据

Datafox(数据狐)

闲鱼数据采集 闲鱼商品列表api 关键词搜索闲鱼接口

从数据噪音到商业信号:专业海外舆情分析服务的价值转化路径

沃观Wovision

舆情分析 舆情监测 海外舆情

电商图片搜索:技术原理与商业落地场景深度解析

Noah

百度智能云发布新一代昆仑芯和天池超节点,打造最硬AI云

新消费日报

如何在DApp中实现DAO功能?去中心化治理开发详解

西安链酷科技

助力企业构建 AI 原生应用,函数计算 FunctionAI 重塑模型服务与 Agent 全栈生态

阿里巴巴云原生

阿里云 Serverless 云原生 Function AI

效率提升300%?海外数据筛选的三大核心策略与一个被忽视的技巧

沃观Wovision

数据 数据提取与筛选 海外数据与筛选

AI 原生应用开发实战营·深圳站丨限时报名开启!

阿里巴巴云原生

阿里云 Serverless RocketMQ 微服务 云原生

交易所开发Java交易所RWA交易所开发不动产上链发行app开发公司

西安链酷科技

出海舆情监测网站选型必须考察的8个关键要素

沃观Wovision

舆情监测 舆情监测网站 出海舆情

一拍即存!小红书爆款一键提取到飞书多维表格含快捷指令

阿星AI工作室

产品 AI 自媒体 小红书 选题

区块链 NFT 项目的上线

北京木奇移动技术有限公司

区块链开发 软件外包公司 web3开发

从数据到决策:海外舆情监测全托管服务

沃观Wovision

海外舆情 海外舆情监测 舆情监测平台 舆情监测服务

2025企业可观测平台选型指南:聚焦核心能力,构建面向未来的观测体系

博睿数据

智能运维 可观测 #运维 一体化可观测平台

华秋电子即将亮相2025高交会,硬核展品+精美好礼等你来打卡!

华秋电子

数据分析案例详解:基于smardaten实现智慧交通运营指标数据分析展示

数睿数据

Java 后端

区块链/Web3 项目开发和运营

北京木奇移动技术有限公司

区块链开发 软件外包公司 web3开发

基于隐语SecretFlow——TrustFlow的数据要素跨域管控

隐语SecretFlow

可信软件 可信计算 开源隐私计算框架 可信执行环境

什么是CEX(中心化交易所)?

西安链酷科技

中国燃放生命健康国际创新研究院揭牌成立

科技汇

企业级智能问数四问:从“语义鸿沟”到“统一认知”

Aloudata

数据分析 ChatBI 智能问数 智能归因 dataagent

openFuyao亮相2025沙中开源与AI科技峰会,共建全球多样化算力生态未来

openFuyao

2025中国密码学会年会“人才培养论坛”成功举办,产学共探密码人才培育新路径

隐语SecretFlow

Web3 DAO 开发全流程实战:从治理机制设计到社交平台适配的去中心化组织构建

西安链酷科技

海外数据筛选实战指南:从杂乱信息到精准数据的五步法

沃观Wovision

数据分析 数据 海外数据与筛选

即时通讯软件泄密不止,国产化企业IM软件BeeWorks保证企业数据安全

BeeWorks

即时通讯 IM 私有化部署

区块链游戏开发核心技术

北京木奇移动技术有限公司

区块链游戏 区块链开发 软件外包公司

2026版出海舆情监测网站选型白皮书:四大核心维度评估模型

沃观Wovision

跨境贸易 出海 舆情监测网站 出海舆情

AI 英语学习 APP 的运营

北京木奇移动技术有限公司

AI教育 软件外包公司 AI英语学习

重磅认可! 绿盟科技荣获 2024 年度北京市科学技术进步一等奖

科技经济

数据堂电力行业AI平台建设与高质量多模态数据赋能实践

数据堂

人工智能 数据标注 标注平台 能源电力 高质量数据集建设

Java JVM 可观测的原理解释和落地方案对比_编程语言_InfoQ精选文章