Solución:
De forma predeterminada, se puede acceder a JMX de forma local de forma automática, por lo que ejecutar jconsole
en la zona detectaría todas sus aplicaciones Java locales sin exposición de puertos.
Para acceder a una aplicación a través de JMX remotamente debe especificar un puerto de registro RMI. Lo que debe saber es que cuando se conecta, JMX se inicializa en ese puerto y luego establece una conexión de datos en un alto aleatorio puerto, que es un gran problema si tiene un firewall en el medio. (“Hola, administradores de sistemas, abran todo, ¿verdad?”).
Para obligar a JMX a volver a conectarse en el mismo puerto que ha establecido, tiene un par de las siguientes opciones. Nota: puede usar diferentes puertos para JMX y RMI o puede usar el mismo puerto.
Opción 1: línea de comandos
-Dcom.sun.management.jmxremote.port=$JMX_REGISTRY_PORT
-Dcom.sun.management.jmxremote.rmi.port=$RMI_SERVER_PORT
Si está utilizando Spring Boot, puede poner esto en su (appname).conf
archivo que vive junto a tu (appname).jar
despliegue.
Opción 2: configuración de Tomcat / Tomee
Configure un JmxRemoteLifecycleListener:
Tarro de Maven:
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-catalina-jmx-remote</artifactId>
<version>8.5.9</version>
<type>jar</type>
</dependency>
Configure su server.xml:
<Listener className="org.apache.catalina.mbeans.JmxRemoteLifecycleListener"
rmiRegistryPortPlatform="10001" rmiServerPortPlatform="10002" />
Opción 3: configurar programáticamente
@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;
}
}
El truco, verás, es el serviceUrl
en el que especifica tanto el host / puerto jmx: rmi como el host / puerto jndi: rmi. Si especifica ambos, no obtendrá el “problema” alto aleatorio.
Editar: Para que funcione la comunicación remota JMX, deberá tomar una decisión sobre la autenticación. Es mejor hacerlo en 3 pasos distintos: 1) configuración básica con -Dcom.sun.management.jmxremote.authenticate=false
luego 2) agregue un archivo de contraseña (-Dcom.sun.management.jmxremote.password.file
). Consulte aquí las instrucciones. + -Dcom.sun.management.jmxremote.ssl=false
y luego 3) configurar SSL.
Agregue las siguientes propiedades de JVM en “$ JAVA_OPTS” (en su aplicación):
-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>
En Jconsole / Visual VM, use lo siguiente para conectarse:
service:jmx:rmi:///jndi/rmi://<HOST'S_IP>:<PORT_NUMBER>/jmxrmi
No habilita la seguridad, pero le ayudará a conectarse al servidor remoto.
Un enfoque probado en Java 1.8.0_71 y Spring Boot (1.3.3.RELEASE). Añada los siguientes parámetros a los argumentos de la JVM para la JVM supervisada.
-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
los com.sun.management.jmxremote.port
se utiliza para definir el puerto de registro RMI fijo, y el com.sun.management.jmxremote.rmi.port
se utiliza para indicar a JVM que utilice un puerto RMI fijo, pero NO utilice uno aleatorio.
Al configurar esto, puedo conectar el cliente JVM desde el host remoto a la JVM monitoreada a través de un firewall que acaba de abrir los puertos 12348 y 12349.
Probé usando java -jar cmdline-jmxclient-0.10.3.jar user:pwd hostip:12348
en una máquina remota, que genera la siguiente salida (abreviada solo para demostración).
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
.......
El frasco se descarga desde aquí.