我知道 spring 自动公开 JMX beans。我能够使用 VisualVM 在本地访问它。
但是在产品上我如何使用它的 JMX beans 远程连接到应用程序?是否有默认端口或者我应该另外定义什么?
谢谢, 雷.
默认情况下,JMX 可在本地自动访问,因此运行
jconsole
locally 将检测所有本地 Java 应用程序,而无需暴露端口。
要通过 JMX 远程访问应用程序,您必须指定 RMI 注册表端口。要知道的是,连接时,JMX 在该端口上初始化,然后然后 在随机高端口上建立数据连接,如果中间有防火墙,这将是一个巨大的问题。 (“嘿,系统管理员,打开所有内容,好吗?”)。
要强制 JMX 连接回您已建立的同一端口,您有以下几个选项。注意:您可以为 JMX 和 RMI 使用不同的端口,也可以使用相同的端口。
选项 1:命令行
-Dcom.sun.management.jmxremote.port=$JMX_REGISTRY_PORT
-Dcom.sun.management.jmxremote.rmi.port=$RMI_SERVER_PORT
如果您使用 Spring Boot,您可以将其放入与
(appname).conf
部署一起存在的 (appname).jar
文件中。
选项2:Tomcat/Tomee配置
配置一个JmxRemoteLifecycleListener:
Maven 罐子:
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina-jmx-remote</artifactId>
<version>8.5.9</version>
<type>jar</type>
</dependency>
配置您的server.xml:
<Listener className="org.apache.catalina.mbeans.JmxRemoteLifecycleListener"
rmiRegistryPortPlatform="10001" rmiServerPortPlatform="10002" />
选项 3:以编程方式配置
@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 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;
}
}