一、背景
性能测试需要监控服务端 JVM 信息,Java 虚拟机 (JVM) 提供操作管理和监测提供了一套完整框架,即 JMX(Java 管理扩展),我们需要做到采集其所暴露出来的性能指标。
二、什么是JMX?
JMX 技术定义了完整的架构和设计模式集合,以对 Java 应用进行监测和管理。JMX 的基础是托管豆(managed bean,业内更习惯将其称为 MBean),MBean 是通过依赖注入完成实例化的各个类别,代表着 JVM 中的资源。由于 MBean 代表 JVM 中的资源,所以我们可以用其来管理应用的特定方面,或者更为常见的一种做法,用其来收集与这些资源的使用相关的统计数据。JMX 的核心是 MBean 服务器,此类服务器可以作为媒介将 MBean、同一 JVM 内的应用以及外部世界联系在一起。与 MBean 之间的任何交互都是通过此服务器完成的。通常而言,只有 Java 代码能够直接访问 JMX API,但是有一些适配器可将该 API 转换为标准协议,例如 Jolokia 便可将其转换为 HTTP。
三、什么是Jolokia?
Jolokia 作为目前最主流的 JMX 监控组件,spring 社区(springboot、MVC、cloud)以及目前主流的中间件服务均采用它作为 JMX 监控,Jolokia 是无类型的数据,使用了 Json 这种轻量化的序列化方案来替代 RMI 方案。
四、Jolokia的特点?
- JMX 可以实现 VM 内部运行时数据状态的对外 export,我们通过将运行态数据封装成 MBean,通过 JMX Server 统一管理,并允许外部程序通过 RMI 方式获取数据。总之,JMX允许运行态数据通过 RMI 协议被外部程序获取。这对我们监控、操作 VM 内部数据提供窗口。
- JMX 扩展性、可实施能力非常强大,但是其问题就是如果获取 MBean 数据,需要使用 JAVA 栈的 RMI 协议,这对外部程序比如监控组件(非JAVA栈)支持不够良好。
- Jolokia 完全兼容并支撑 JMX 组件,它可以作为 agent 嵌入到任何 JAVA 程序中,特别是 WEB 应用,它将复杂而且难以理解的 MBean Filter 查询语句,转换成更易于实施和操作的 HTTP 请求范式,不仅屏蔽了 RMI 的开发困难问题,还实现了对外部监控组件的透明度,而且更易于测试和使用。
- 直观来说,Jolokia 就是用于解决 JMX 数据获取时,所遇到的 RMI 协议复杂性、Mbean 查询的不便捷、数据库序列化、MBeanServer 的托管等问题
- 我们只需要使用 HTTP 请求,直接访问与 WEB 服务相同的 port 即可获取 JMX 数据。
五、选型考虑
- 由于在服务端在集群化的弹性环境中,考虑未来微服务下节点大量增长、扩展,并由非常多的应用实例所组成。对于单独节点的监控可能即费力又没有什么实际效果。所以,使用基于时间序列的数据聚合方式将获得更好的效果。
- Spring Boot & Spring MVC 认可使用 Jolokia 来通过 HTTP 导出 export JMX 数据。只需要在工程类路径中增加一些依赖项,一切都是开箱即用的。不需要任何额外的实现。
- Telegraf 支持通过整合 Jolokia 来集成 JMX 数据的收集。它有一个预制的输入插件,它是开箱即用的。不需要任何额外的实现。只需要做一些配置即可。
- InfluxDB 通过输出插件从 Telegraf 接收指标数据,它是开箱即用的,不需要任何额外的实现。
- Grafana 通过连接 InfluxDB 作为数据源来渲染 Dashboard。它是开箱即用的,不需要额外的实现。
六、Jolokia & 服务端集成
1、Jolokia Agent模式
Agent 可以调用本地的 MBeanServer 暴露 Restful 接口供外部调用,在客户端上可以应用不同的技术来展示通过 Http 获取的 JMX 数据
Agent模式主要有以下的方式:
方法一:是将 jolokia 放置到 servlet 容器中,比如 Tomcat 或 Jetty,这样 Jolokia 完全可以看做是一个常规的 Java web 应用,让所有的开发人员都能够很好理解并快速的从中读取数据,如下:
[root@localhost webapps]# pwd /usr/local/src/apache-tomcat-7.0.73/webapps [root@localhost webapps]# ls -l total 83428 drwxr-xr-x 14 root root 4096 Jun 28 2018 docs drwxr-xr-x 7 root root 105 Jun 28 2018 examples drwxr-xr-x 5 root root 82 Jun 28 2018 host-manager drwxr-xr-x 4 root root 35 Apr 5 2019 jolokia -rw-r--r-- 1 root root 307617 Nov 9 2014 jolokia.war drwxr-xr-x 5 root root 97 Jun 28 2018 manager drwxr-xr-x 3 root root 4096 Jun 28 2018 ROOT drwxr-xr-x 3 root root 22 Mar 22 2019 v3cAPITestReport drwxr-xr-x 7 root root 4096 Jun 28 2018 visu1021 -rw-r--r-- 1 root root 85103469 Jun 28 2018 visu1021.war
方法二: 除了放到 Servlet 容器之外,Jolokia 也可以定义特殊的 Agent,比如实现 OSGi 或者内置 Jetty 服务器
- 方法三:Jolokia 也可以集成到 Web 应用中,
jolokia-core
库作为一个 Jar 包,提供一个 Servlet,加入到 Web 应用中之后就可以访问。
考虑到集群部署及目前服务端现状,推荐第三种方式。
2、jolokia-core 集成示例
2.1、SpringMVC
主要步骤:
pom.xml
中增加 jolokia 依赖。- 在
web.xml
中声明jolokia servlet
启动和适配。 - 在 resources 目录下增加
jolokia-access.xml
安全访问 - 在
spring xml
文件中增加相关MBean export显示操作。
我们需要在 pom.xml
中增加 jolokia 的依赖,使用最新版本。
<!-- jolokia 核心组件 -->
<dependency>
<groupId>org.jolokia</groupId>
<artifactId>jolokia-core</artifactId>
<version>1.6.1</version>
</dependency>
需要注意,jolokia 作为嵌入式 agent,将会与我们 web 容器一起启动,jolokia agent 与 web 服务共享一个 HTTP 端口,由此 servlet 负责承担请求解析。此后可以通过 “/jolokia”
来访问内部的 JMX 数据
<!-- jolokia 监控 -->
<servlet>
<servlet-name>jolokia‐agent</servlet-name>
<servlet-class>org.jolokia.http.AgentServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>jolokia‐agent</servlet-name>
<url-pattern>/jolokia/*</url-pattern>
</servlet-mapping>
jolokia 以 servlet 服务提供给外部程序,那么意味着我们可以通过 URL 获取数据,在很多时候我们不希望这些数据被外部非法用户获取、只对内部监控组件开发,比如不希望用户通过 “域名 + /jolokia”
来获取数据等。此 jolokia-access.xml 表示,只允许 "127.0.0.1"
即本地的监控组件可以获取数据,对于跨机器、代理程序均无法获取。这也要求我们的 telegraf
探针是部署在WEB应用的宿主机器上。
<?xml version="1.0" encoding="UTF-8"?>
<restrict>
<!-- 若需限制请自行配置 -->
<!-- <host>127.0.0.1</host> -->
</restrict>
此后,我们将也可以通过如下 http 接口查看 jolokia 的是否正常
2.2、SpringBoot
Springboot 项目,对 endpoint 管理更加智能化和全面,jmx 的支持很封装也更加完善,所以实现 jmx 监控更加便捷。(建议关注acturator组件)
主要步骤:
- pom.xml 中增加 acturator 组件的引入,包括 jolokia 组件。
- 利用 springboot 自动装配,我们开启
“management”
、“endpoint”
功能即可。 - 我们不再需要
web.xml
以及jolokia-access.xml
,因为这些都是默认支持的。(自动装配)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- jolokia 核心组件 -->
<dependency>
<groupId>org.jolokia</groupId>
<artifactId>jolokia-core</artifactId>
<version>1.6.1</version>
</dependency>
#jolokia
management:
security:
enabled: false
address: 127.0.0.1
endpoints:
jolokia:
enabled: true
我们在 application.yml
文件中增加相应的配置,特别注意 management 和 endpoint 部分。
七、Telegraf 配置
Telegraf 的 Jolokia2 输入插件支持使用 JSON-over-HTTP
协议从一个或多个Jolokia代理REST端点读取JMX指标数据。
[[inputs.jolokia2_agent]]
urls = ["http://agent:8080/jolokia"]
[[inputs.jolokia2_agent.metric]]
name = "jvm_runtime"
mbean = "java.lang:type=Runtime"
paths = ["Uptime"]
每个度量声明生成一个 Jolokia 请求,以便从 JMX MBean
获取指标数据。
|Key | Required | Description |
|----------------|----------|:-------------|
| mbean
| yes | JMX MBean 的对象名。MBean 属性键值可以包含通配符 *,允许通过一个声明获取多个 MBean |
| paths
| no | 要读取的 MBean 属性列表。|
| tag_keys
| no | 要转换为标记的 MBean 属性键名称列表。属性键名成为标记名,而属性键值成为标记值。 |
| tag_prefix
| no | 在此指标声明生成的标记名称之前的字符串。 |
| field_name
| no | 要设置为由该度量生成的字段的名称的字符串;可以替换。 |
| field_prefix
| no | 在此指标声明产生的字段名之前的字符串;可以替换。 |
JVM配置如下:
# # Read JMX metrics from a Jolokia REST agent endpoint
[[inputs.jolokia2_agent]]
urls = ["http://localhost:8089/jolokia"]
[[inputs.jolokia2_agent.metric]]
name = "java_runtime"
mbean = "java.lang:type=Runtime"
paths = ["Uptime"]
[[inputs.jolokia2_agent.metric]]
name = "java_memory"
mbean = "java.lang:type=Memory"
paths = ["HeapMemoryUsage", "NonHeapMemoryUsage", "ObjectPendingFinalizationCount"]
[[inputs.jolokia2_agent.metric]]
name = "java_garbage_collector"
mbean = "java.lang:name=*,type=GarbageCollector"
paths = ["CollectionTime", "CollectionCount"]
tag_keys = ["name"]
[[inputs.jolokia2_agent.metric]]
name = "java_last_garbage_collection"
mbean = "java.lang:name=*,type=GarbageCollector"
paths = ["LastGcInfo"]
tag_keys = ["name"]
[[inputs.jolokia2_agent.metrics]]
name = "java_threading"
mbean = "java.lang:type=Threading"
paths = ["TotalStartedThreadCount", "ThreadCount", "DaemonThreadCount", "PeakThreadCount"]
[[inputs.jolokia2_agent.metrics]]
name = "java_class_loading"
mbean = "java.lang:type=ClassLoading"
paths = ["LoadedClassCount", "UnloadedClassCount", "TotalLoadedClassCount"]
[[inputs.jolokia2_agent.metrics]]
name = "java_memory_pool"
mbean = "java.lang:name=*,type=MemoryPool"
paths = ["Usage", "PeakUsage", "CollectionUsage"]
tag_keys = ["name"]
其他配置可以参考官方示例:
https://github.com/influxdata/telegraf/tree/master/plugins/inputs/jolokia2/examples
InfluxDB 采集的数据如下:
> show measurements
name: measurements
name
----
java_class_loading
java_garbage_collector
java_memory
java_memory_pool
java_runtime
java_threading
> select * from java_memory limit 5
name: java_memory
time HeapMemoryUsage.committed HeapMemoryUsage.init HeapMemoryUsage.max HeapMemoryUsage.used NonHeapMemoryUsage.committed NonHeapMemoryUsage.init NonHeapMemoryUsage.max NonHeapMemoryUsage.used ObjectPendingFinalizationCount host jolokia_agent_url
---- ------------------------- -------------------- ------------------- -------------------- ---------------------------- ----------------------- ---------------------- ----------------------- ------------------------------ ---- -----------------
1571105290000000000 3344433152 2147483648 7635730432 1216965408 172425216 2555904 -1 169201160 0 DESKTOP-MLD0KTS http://localhost:8089/jolokia
1571105300000000000 3344433152 2147483648 7635730432 1287744120 172425216 2555904 -1 168805312 0 DESKTOP-MLD0KTS http://localhost:8089/jolokia
1571105310000000000 3344433152 2147483648 7635730432 1359803320 172425216 2555904 -1 168981192 0 DESKTOP-MLD0KTS http://localhost:8089/jolokia
1571105320000000000 3344433152 2147483648 7635730432 1434671784 172425216 2555904 -1 169114552 0 DESKTOP-MLD0KTS http://localhost:8089/jolokia
1571105330000000000 3344433152 2147483648 7635730432 1509156560 172425216 2555904 -1 169181064 0 DESKTOP-MLD0KTS http://localhost:8089/jolokia
八、Grafana监控效果图
相关资料:
参考资料: