我知道 Spring 自动暴露 JMX beans。我能够使用 VisualVM 在本地访问它。
然而,在生产环境中,我如何使用它的 JMX beans 远程连接到应用程序呢?是否有默认端口或者我需要额外定义一些内容?
谢谢, ray。
我知道 Spring 自动暴露 JMX beans。我能够使用 VisualVM 在本地访问它。
然而,在生产环境中,我如何使用它的 JMX beans 远程连接到应用程序呢?是否有默认端口或者我需要额外定义一些内容?
谢谢, ray。
-Dcom.sun.management.jmxremote.port=$JMX_REGISTRY_PORT
-Dcom.sun.management.jmxremote.rmi.port=$RMI_SERVER_PORT
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina-jmx-remote</artifactId>
<version>8.5.9</version>
<type>jar</type>
</dependency>
<Listener className="org.apache.catalina.mbeans.JmxRemoteLifecycleListener"
rmiRegistryPortPlatform="10001" rmiServerPortPlatform="10002" />
@Configuration
public class ConfigureRMI {
@Value("${jmx.rmi.host:localhost}")
private String rmiHost;
@Value("${jmx.rmi.port:1099}")
private Integer rmiPort;
@Bean
public RmiRegistryFactoryBean rmiRegistry() {
final RmiRegistryFactoryBean rmiRegistryFactoryBean = new RmiRegistryFactoryBean();
rmiRegistryFactoryBean.setPort(rmiPort);
rmiRegistryFactoryBean.setAlwaysCreate(true);
return rmiRegistryFactoryBean;
}
@Bean
@DependsOn("rmiRegistry")
public ConnectorServerFactoryBean connectorServerFactoryBean() throws Exception {
final ConnectorServerFactoryBean connectorServerFactoryBean = new ConnectorServerFactoryBean();
connectorServerFactoryBean.setObjectName("connector:name=rmi");
connectorServerFactoryBean.setServiceUrl(String.format("service:jmx:rmi://%s:%s/jndi/rmi://%s:%s/jmxrmi", rmiHost, rmiPort, rmiHost, rmiPort));
return connectorServerFactoryBean;
}
}
serviceUrl
中指定jmx:rmi主机/端口和jndi:rmi主机/端口。如果你同时指定了两者,就不会出现随机高的“问题”。
编辑:为了使JMX远程工作,你需要做一个关于身份验证的决定。最好分为3个明确的步骤来完成:
-Dcom.sun.management.jmxremote.authenticate=false
,然后-Dcom.sun.management.jmxremote.password.file
)。请参阅此处的说明。 + -Dcom.sun.management.jmxremote.ssl=false
,然后在您的应用程序中的"$JAVA_OPTS"中添加以下JVM属性:
-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=<PORT_NUMBER> -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=<HOST'S_IP>
在Jconsole/Visual VM中使用以下内容进行连接:
service:jmx:rmi:///jndi/rmi://<HOST'S_IP>:<PORT_NUMBER>/jmxrmi
它没有启用安全功能,但可以帮助您连接到远程服务器。
java
命令行使用。在Spring Boot 2.3.2中,在JAVA_OPTS中设置它们对我没有起作用。这里有一篇更长的博客文章:https://medium.com/@cl4r1ty/docker-spring-boot-and-java-opts-ba381c818fa2。(Docker无关紧要,`JAVA_OPTS="..." java -jar`也不起作用。) - Dima Korobskiy在Java 1.8.0_71和Spring Boot(1.3.3.RELEASE)上测试过的方法。将以下参数添加到监控JVM的JVM参数中。
-Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=12348 -Dcom.sun.management.jmxremote.authenticate=true -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.rmi.port=12349 -Dcom.sun.management.jmxremote.password.file=/somewhere/jmxremote.password -Dcom.sun.management.jmxremote.access.file=/somewhere/jmx/jmxremote.access
com.sun.management.jmxremote.port
用于定义固定的RMI注册表端口,com.sun.management.jmxremote.rmi.port
则用于指示JVM使用固定的RMI端口而非随机端口。通过设置这些参数,我可以通过仅打开12348和12349端口的防火墙连接来自远程主机的JVM客户端监视受监视的JVM。我在远程计算机上使用java -jar cmdline-jmxclient-0.10.3.jar user:pwd hostip:12348
进行测试,生成以下输出(仅为演示而缩短)。java.lang:type=Runtime
java.lang:name=PS Scavenge,type=GarbageCollector
Tomcat:J2EEApplication=none,J2EEServer=none,WebModule=//localhost/,j2eeType=Filter,name=requestContextFilter
java.nio:name=mapped,type=BufferPool
Tomcat:host=localhost,type=Host
java.lang:name=Compressed Class Space,type=MemoryPool
.......
这个jar包可以从这里下载。
另一种选择
jmxremote.password和jmxremote.access文件的参考
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.jmx.support.ConnectorServerFactoryBean;
import org.springframework.remoting.rmi.RmiRegistryFactoryBean;
@Configuration
public class ConfigureRMI {
@Value("${jmx.rmi.password.file:/tmp/jmxremote.password}")
private String passwordFile;
@Value("${jmx.rmi.access.file:/tmp/jmxremote.access}")
private String accessFile;
@Value("${jmx.rmi.port:19999}")
private Integer rmiPort;
@Bean
public RmiRegistryFactoryBean rmiRegistry() {
final RmiRegistryFactoryBean rmiRegistryFactoryBean = new RmiRegistryFactoryBean();
rmiRegistryFactoryBean.setPort(rmiPort);
rmiRegistryFactoryBean.setAlwaysCreate(true);
return rmiRegistryFactoryBean;
}
@Bean
@DependsOn("rmiRegistry")
public ConnectorServerFactoryBean connectorServerFactoryBean() throws Exception {
final ConnectorServerFactoryBean connectorServerFactoryBean = new ConnectorServerFactoryBean();
connectorServerFactoryBean.setObjectName("connector:name=rmi");
Map<String, Object> properties = new HashMap<>();
properties.put("jmx.remote.x.password.file", passwordFile);
properties.put("jmx.remote.x.access.file", accessFile);
connectorServerFactoryBean.setEnvironmentMap(properties);
connectorServerFactoryBean.setServiceUrl(String.format("service:jmx:rmi:///jndi/rmi://:%s/jmxrmi", rmiPort));
return connectorServerFactoryBean;
}
}
JAVA_OPTS
方法? - raffianjmxremote.password
文件。将标志设置为 false 是一种安全漏洞,除非您在非生产机器上使用,否则不要这样做。 - inanutshellus