声明:相关运行样例均使用JDK 8
JMX
JMX(Java Management Extension)在官网的介绍是:
The JMX technology provides the tools for building distributed, Web-based, modular and dynamic solutions for managing and monitoring devices, applications, and service-driven networks. By design, this standard is suitable for adapting legacy systems, implementing new management and monitoring solutions, and plugging into those of the future.
这里我们可以简单理解为:JMX是Java平台为设备、应用程序、服务等植入监控和管理功能的一个标准框架。其中一个比较基础的概念就是MBean(Management Bean),它是JavaBean的一种,用于管理Java Object。JMX不是这篇文章的重点,回头我再写一些基础文章继续介绍。
JVM监控
我们知道,jdk中自带了很多jvm的监控工具,比如jstat,jvisualvm,jconsole等。
如果是本地程序,直接运行对应的命令就可以观察了,比如:
➜ 2021 >jps
21248
21857 Launcher
21864 Jps
21855 MainTest
➜ 2021 >jvisualvm --openpid 21855
2021-01-29 08:35:34.783 java[22253:518950] *** WARNING: Textured window <AWTWindow_Normal: 0x7fcbf1fa3ba0> is getting an implicitly transparent titlebar. This will break when linking against newer SDKs. Use NSWindow's -titlebarAppearsTransparent=YES instead.
➜ 2021 >
➜ 2021 >jconsole 21855
➜ 2021 >
但如果我们需要观察测试或生产等远程环境的Java进程时,就需要在启动java进程时启动JVM参数,示例如下:
java -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=<port> -Djava.rmi.server.hostname=<IPAddress> -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -jar xxx.jar
然后就可以通过终端访问了,比如:
# 当然也可以直接使用jconsole不带参数,然后在可视化窗口中填入对应的地址。
➜ 2021 >jconsole 192.168.1.22:9001
官网上对相关配置参数有详细的说明,我这里做了一个简单的复制:
option | description | default value | example |
---|---|---|---|
com.sun.management.jmxremote | 允许远程JMX client 访问本机jvm | true | -Dcom.sun.management.jmxremote |
com.sun.management.jmxremote.port | 允许远程JMX client 访问本机jvm的端口 | -Dcom.sun.management.jmxremote.port=9009 | |
com.sun.management.jmxremote.authenticate | 是否放开权限校验 | -Dcom.sun.management.jmxremote.authenticate=false | |
com.sun.management.jmxremote.ssl | 是否开启ssl校验 | true | -Dcom.sun.management.jmxremote.ssl=false |
com.sun.management.jmxremote.login.config | JMX agent to use the specified JAAS configuration entry, LDAP权限登录配置 | -Dcom.sun.management.jmxremote.login.config=ExampleCompanyConfig | |
java.security.auth.login.config | specifies the path to the JAAS configuration file, LDAP权限登录配置 | -Djava.security.auth.login.config=ldap.config | |
com.sun.management.jmxremote.password.file | 基于文件密码的登录校验配置, 设置访问用户的用户名和密码 | 默认路径JRE_HOME/lib/management/ jmxremote.password | -Dcom.sun.management.jmxremote.password.file=pwFilePath |
com.sun.management.jmxremote. registry.ssl | Binds the RMI connector stub to an RMI registry protected by SSL | false | -Dcom.sun.management.jmxremote. registry.ssl=true |
com.sun.management.jmxremote. ssl.enabled.protocols | A comma-delimited list of SSL/TLS protocol versions to enable. Used in conjunction with com.sun.management.jmxremote.ssl | ||
com.sun.management.jmxremote. ssl.enabled.cipher.suites | A comma-delimited list of SSL/TLS cipher suites to enable. Used in conjunction with com.sun.management.jmxremote.ssl | ||
com.sun.management.jmxremote. ssl.need.client.auth | If this property is true and the property com.sun.management.jmxremote.ssl is also true, then client authentication will be performed.It is recommended that you set this property to true | false | |
com.sun.management.jmxremote.access.file | Specifies location for the access file,对访问用户的权限授权的文件的路径 | 默认路径JRE_HOME/lib/management/jmxremote.access |
最近我在监控测试环境JVM的性能时,通过jconsole访问时,总是连接不上,Google之后找到了相关文章,才知道:
The initial port you define with 'com.sun.management.jmxremote.port' is called a registry port and is only used to start negotiation and determine next port(s) to use for "real" communication. Java RMI mechanism uses dynamically allocated ports and in general is not compatible with firewalls.
也就是说,上面配置的 com.sun.management.jmxremote.port=
还好从Java 7开始,官方给出了一个配置,将这个随机端口给固定住,并且建议其值与 com.sun.management.jmxremote.port 配置的值相同。
-Dcom.sun.management.jmxremote.rmi.port=<port>
果然,配置之后,问题就解决,完整示例如下:
java -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=<port> -Dcom.sun.management.jmxremote.rmi.port=<port> -Djava.rmi.server.hostname=<IPAddress> -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -jar xxx.jar
如果是监控tomcat,则可以将上面的配置放入setenv.sh(位于${TomcatPath}/bin/)中:
export CATALINA_OPTS="${CATALINA_OPTS} -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=<port> -Dcom.sun.management.jmxremote.rmi.port=<port> -Djava.rmi.server.hostname=<IPAddress> -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false"
相关资料中说 java.rmi.server.hostname 这项配置不是必须的,经过测试,确实可以省略。另外com.sun.management.jmxremote的默认值就是true,所以也可以省略,我测试了一个不考虑防火墙拦截的简化版例子如下:
java -Dcom.sun.management.jmxremote.port=<port> -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -jar xxx.jar