¿Java 6 abre un puerto predeterminado para conexiones remotas JMX?


Mi pregunta específica tiene que ver con JMX como se usa en JDK 1.6: si estoy ejecutando un proceso Java usando JRE 1.6 con

com.sun.management.jmxremote

En la línea de comandos, ¿Java elige un puerto predeterminado para conexiones JMX remotas?

Historia de fondo: Actualmente estoy tratando de desarrollar un procedimiento para dar a un cliente que les permita conectarse a uno de nuestros procesos a través de JMX desde una máquina remota. El objetivo es facilitar la depuración remota de una situación que ocurre en una pantalla en tiempo real consola. Debido a su acuerdo de nivel de servicio, están fuertemente motivados para capturar la mayor cantidad de datos posible y, si la situación parece demasiado complicada para solucionarla rápidamente, para reiniciar la consola de visualización y permitir que se vuelva a conectar al lado del servidor.

Soy consciente de que podría ejecutar jconsole en procesos JDK 1.6 y jvisualvm en procesos post-JDK 1.6.7 con acceso físico a la consola. Sin embargo, debido a las necesidades operacionales y los problemas de las personas involucrados, estamos fuertemente motivados para tomar los datos que necesitamos de forma remota y ponerlos en marcha de nuevo.

EDITAR: Soy consciente de la propiedad puerto de línea de comandos

com.sun.management.jmxremote.port=portNum

La pregunta que estoy tratando de responder es, si no establece esa propiedad en la línea de comandos, ¿Java elige otro puerto para la supervisión remota? Si es así, ¿cómo podría determinar lo que podría ser?

Author: Bob Cross, 2009-02-05

7 answers

La documentación sugiere que el agente JMX usa un puerto local something algo inalcanzable desde fuera de la máquina unless a menos que especifique la siguiente propiedad:

com.sun.management.jmxremote.port=portNum

Esto es por razones de seguridad, así como por la razón dada por el Sr. Potato Head. Por lo tanto, parece que Java 6 no abre un puerto predeterminado remotamente accesible para JMX.

EDITAR: Se agregó después de que el OP agregara una respuesta con más información.

Otra opción tiene que crear de alguna manera un proxy local que escuche todas las conexiones JMX locales y exporte esta información. De esta manera, no es necesario tener tal configuración mágica de cada instancia de JVM en el servidor. En su lugar, el proxy local puede conectarse a todas las JVM a través de JMX y, de alguna manera, exponer esta información de forma remota. No estoy seguro exactamente cómo implementaría esto, pero algo como esto puede ser menos trabajo que lo que de otra manera tendría que hacer para exponer todas sus JVM de forma remota a través de JMX.

 37
Author: Eddie,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-10 04:53:48

AFAIK,

Aquí están las posibilidades para conectar un proceso cliente JMX (una aplicación de administración como jconsole, jmxterm, mc4j, jvmstat, jmxmonitor, jps, ...) a un proceso del servidor JMX (el agente ).

Se asume que el protocolo que conecta el cliente JMX y el servidor JMX es 'Java RMI' (también conocido como 'RMI-JRMP'). Este debería ser el valor predeterminado. Se pueden configurar otros protocolos, en particular 'RMI-IIOP' y 'JMXMP'. Protocolos especiales son posibles: el proyecto MX4J, por ejemplo, proporciona adicionalmente SOAP/HTTP y varios protocolos de serialización sobre HTTP.

Consulte Sun/Oracle docs para obtener más información sobre la configuración.

También eche un vistazo al archivo jre/lib/management/management.properties en su distribución JDK.

Entonces, las posibilidades:

Caso 0: La JVM se inicia sin ninguna configuración particular

Antes de Java 6: La JVM no se comporta como una JMX servidor. Cualquier programa que se ejecuta dentro de la JVM puede acceder al MBeanServer de la JVM mediante programación y usarlo para realizar intercambios de datos interesantes entre subprocesos o para hacer monitoreo de la JVM, pero no es posible la administración desde fuera del proceso de la JVM.

Desde Java 6: Incluso si no está explícitamente configurado, se puede acceder a la funcionalidad JMX de la JVM localmente (desde la misma máquina) como se describe en "Caso 1".

Caso 1: La JVM se inicia con -Dcom.sun.management.jmxremote

La JVM está configurada para funcionar como un servidor JMX local (solo para la misma máquina).

En este caso (y en principio solo para Sun/Oracle JVMs) un cliente JMX puede conectarse al servidor JMX a través de archivos mapeados de memoria que se encuentran en /tmp/hsperfdata_[user]. Esto se alude en la documentación de Sun y se llama "monitoreo local" (y también la API Attach). No funciona en sistemas de archivos FAT ya que los permisos no se pueden establecer correctamente allí. Ver este blog entrada .

Sun recomienda ejecutar jconsole en una máquina separada del servidor JMX ya que jconsole aparentemente es un acaparamiento de recursos, por lo que esta cosa de "monitoreo local" no es necesariamente una buena idea.

El monitoreo local es, sin embargo, bastante seguro, solo se puede usar localmente y se puede controlar fácilmente a través de los permisos del sistema de archivos.

Caso 2: El servidor JMX se inicia con -Dcom.sun.management.jmxremote.port=[rmiregistryport]

La JVM está configurada para funcionar como un servidor JMX escuchando en varios Puertos TCP.

El puerto especificado en la línea de comandos será asignado por la JVM y un registro RMI estará disponible allí. El registro anuncia un conector llamado 'jmxrmi'. Apunta a un segundo puerto TCP asignado aleatoriamente (un puerto 'efímero') en el que el servidor JMX RMI escucha y a través del cual se lleva a cabo el intercambio real de datos.

Local como se describe en 'Caso 1' siempre está habilitado en 'Caso 2'.

El servidor JMX escucha en todas las interfaces de forma predeterminada, por lo que puede conectarse a él (y controlarlo) conectándose localmente a 127.0.0.1:[rmiregistryport] así como conectándose remotamente a [cualquier dirección IP externa]:[algún puerto] remotamente.

Esto implica que usted tiene que mirar las implicaciones de seguridad. Puede hacer que la JVM escuche en 127.0.0.1: [rmiregistryport] solo configurando -Dcom.sun.management.jmxremote.local.only=true.

Es bastante desafortunado que uno no pueda especificar dónde se asignará el puerto efímero - siempre se elige aleatoriamente al inicio. Este mayo bien significa que su cortafuegos tiene que convertirse en el queso suizo de los condenados! Sin embargo, hay soluciones. En particular, Apache Tomcat establece el puerto efímero del servidor JMX RMI a través de su JMX Remote Lifecycle Listener. El código para realizar esta pequeña magia se puede encontrar en org.apache.catalina.mbeans.JmxRemoteLifecycleListener .

Si utiliza este método, también podría asegurarse de que:

  1. El cliente JMX tiene que autenticarse en el Servidor JMX
  2. El intercambio TCP entre el cliente y el servidor está encriptado usando SSL

Cómo se hace esto se describe en la documentación de Sun / Oracle

Otros enfoques

Puede hacer permutaciones interesantes para evitar tener que usar el protocolo RMI. En particular, podría agregar un motor servlet (como Jetty) a su proceso. Luego agregue servlets que traduzcan internamente algún intercambio basado en HTTP en accesos directos a la JVM MBeanServer. Entonces estaría en 'case 0' pero aún tiene capacidades de administración, posiblemente a través de una interfaz basada en HTML. La consola JBoss JMX es un ejemplo de esto.

Más fuera de tema, podría usar SNMP directamente (algo que no he probado) de acuerdo con este documento.

Mostrar y decir la hora

Y ahora es el momento de algún código para ilustrar un intercambio JXM. Nos inspiramos en un Sunoracle tutorial .

Esto se ejecuta en Unix. Usamos una JVM que está configurada como un servidor JMX usando:

-Dcom.sun.management.jmxremote.port=9001

Usamos lsof para verificar qué puertos TCP mantiene abiertos:

lsof -p <processid> -n | grep TCP

Uno debería ver algo como esto, el puerto de registro y el puerto efímero:

java    1068 user  127u  IPv6 125614246                 TCP *:36828 (LISTEN)
java    1068 user  130u  IPv6 125614248                 TCP *:9001  (LISTEN)

Usamos tcpdump para inspeccionar el intercambio de paquetes entre el cliente JMX y el servidor JMX:

tcpdump -l -XX port 36828 or port 9001

Configuramos un fichero .java.policy en el directorio home para permitir cliente para conectarse de forma remota:

grant {
    permission java.net.SocketPermission 
    "<JMX server IP address>:1024-65535", "connect,resolve";
};

Y luego podemos ejecutar esto y ver lo que sucede:

package rmi;

import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

import javax.management.remote.rmi.RMIConnection;
import javax.management.remote.rmi.RMIServer;

public class Rmi {

    public static void main(String args[]) throws Exception {
        // We need a Security Manager (not necessarily an RMISecurityManager)
        if (System.getSecurityManager() == null) {
            System.setSecurityManager(new SecurityManager());
        }
        //
        // Define a registry (this is just about building a local data structure)
        // 
        final int comSunManagementJmxRemotePort = 9001;
        Registry registry = LocateRegistry.getRegistry("<JMX server IP address>", comSunManagementJmxRemotePort);
        //
        // List registry entries. The client connects (using TCP) to the server on the
        // 'com.sun.management.jmxremote.port' and queries data to fill the local registry structure.
        // Among others, a definition for 'jmxrmi' is obtained.
        //
        System.out.print("Press enter to list registry entries");
        System.in.read();
        String[] names = registry.list();
        for (String name : names) {
            System.out.println("In the registry: " + name);
        }
        //
        // 'Looking up' the entry registered under 'jmxrmi' involves opening and tearing down
        // a TCP connection to the 'com.sun.management.jmxremote.port', as well as a TCP
        // connection to an ephemeral secondary port chosen at server startup.
        // The actual object locally obtained is a "javax.management.remote.rmi.RMIServerImpl_Stub"
        // indicating where the ephemeral port is.
        // "RMIServerImpl_Stub[UnicastRef [liveRef: [endpoint:[$IP:$EPHEMERAL_PORT](remote),objID:[-62fb4c1c:131a8c709f4:-7fff, -3335792051140327600]]]]"        
        //
        System.out.print("Press enter to get the 'jmxrmi' stub");
        System.in.read();
        RMIServer jmxrmiServer = (RMIServer)registry.lookup("jmxrmi");
        System.out.println(jmxrmiServer.toString());
        //
        // Now get a "RMI Connection" to the remote. This involves setting up and tearing
        // down a TCP connection to the ephemeral port. 
        //        
        System.out.print("Press enter to get the 'RMIConnection'");
        System.in.read();
        RMIConnection rcon = jmxrmiServer.newClient(null);
        //
        // Ask away. This involves setting up and tearing
        // down a TCP connection to the ephemeral port. 
        //
        System.out.print("Press enter to get the 'domains'");
        System.in.read();
        for (String domain : rcon.getDomains(null)) {
            System.out.println("Domain: " + domain);
        }
        //
        // Ok, that will do. For serious applications, we better use the higher-level JMX classes
        //
    }   
}
 86
Author: David Tonhofer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-27 01:10:14

En realidad hay una propiedad indocumentada que puede usar para forzar que JMX cree conectores accesibles remotamente en números de puerto aleatorios.

-Dcom.sun.management.jmxremote.authenticate="false" 
-Dcom.sun.management.jmxremote="true" 
-Dcom.sun.management.jmxremote.ssl="false" 
-Dcom.sun.management.jmxremote.port="0"
-Dcom.sun.management.jmxremote.local.only="false"

Las dos últimas propiedades son de mayor importancia.

 6
Author: Ivan Koblik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-11-25 08:22:03

La documentación parece indicar que el agente JMX utiliza un puerto efímero local, a menos que especifique la siguiente propiedad:

com.sun.management.jmxremote.port=portNum

Se evitan los puertos predeterminados porque podría tener muchas aplicaciones java en un sistema, y si hubiera un puerto predeterminado, ¡solo se podría administrar una aplicación! La propiedad de configuración anterior se proporciona para el express purpose de remote management.

Si usted debe insistir en usando un puerto efímero, entonces la URL del agente JMX debe ser accesible desde dentro de la JVM, a través de la siguiente propiedad del sistema (aunque es probable que sea una dirección local):

com.sun.management.jmxremote.localConnectorAddress

Nota : Supongo que siempre se puede abrir un socket en una dirección remotamente disponible y solicitudes de proxy en el socket local, pero el uso de la opción disponible parece mucho más atractivo!

 3
Author: David Grant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-05 15:36:05

Entonces, la respuesta corta a mi pregunta es "no."

Sin embargo, es interesante examinar por qué. Mire la salida netstat de una conexión local válida. Aquí están los puertos que veo abiertos como resultado de un jconsole haciendo una conexión local a sí mismo. Como puede ver, el puerto 1650 es el puerto local que se utiliza para la información JMX:

Proto  Local Address          Foreign Address        State
TCP    Gandalf:1650           Gandalf:1652           ESTABLISHED
TCP    Gandalf:1650           Gandalf:1653           ESTABLISHED
TCP    Gandalf:1650           Gandalf:1654           ESTABLISHED
TCP    Gandalf:1650           Gandalf:1655           ESTABLISHED
TCP    Gandalf:1650           Gandalf:1656           ESTABLISHED
TCP    Gandalf:1652           Gandalf:1650           ESTABLISHED
TCP    Gandalf:1653           Gandalf:1650           ESTABLISHED
TCP    Gandalf:1654           Gandalf:1650           ESTABLISHED
TCP    Gandalf:1655           Gandalf:1650           ESTABLISHED
TCP    Gandalf:1656           Gandalf:1650           ESTABLISHED

Sin embargo, no es suficiente intentar conectar jconsole a localhost:1650. Tristemente, todo lo que le red es una " Conexión fallida: no hay tal objeto en la tabla" mensaje.

Entonces, la conclusión de mi historia original es que, si vamos a facilitar el monitoreo remoto usando JMX para nuestros clientes, realmente necesitamos identificar puertos de acceso remoto individuales únicos para la variedad de procesos Java que se inician en nuestro sistema. Afortunadamente, todo lo que esto requiere es el uso juicioso del argumento VM:

com.sun.management.jmxremote.port=portNum

Donde casi seguramente tendremos un rango secuencial pre-especificado de portNum para que el cliente pueda seleccionar el corregir la aplicación remota utilizando el número de puerto.

 2
Author: Bob Cross,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-02-10 01:54:38

He estado trabajando recientemente para averiguar cómo habilitar la administración remota de JMX desde código java, sin requerir que la JVM se haya iniciado con propiedades especiales establecidas. La solución que decidí es iniciar mi propio registro privado de RMI easy bastante fácil and y exponer el servicio JMX en ese registro. Creo mi propio MBeanServer, luego creo un nuevo JMXConnectorServer. El JMXConnectorServer se crea a través de una llamada como

connector = JXMConnectorServerFactory.newJMXConnectorServer(url, null, server);

Donde el servidor es el MBeanServer, y la url es un instancia de JMXServiceURL.

La url es de la forma "service:jmx:rmi:///jndi/rmi://localhost:/jmxrmi" donde port es el número de puerto del registro privado (local). "jmxrmi" es el nombre de servicio estándar para el servicio JMX.

Después de configurar esto e iniciar el conector, encuentro que puedo conectarme a él desde jconsole usando hostname:port.

Esto aborda completamente mis necesidades; estaré interesado en saber si alguien ve un defecto en este enfoque.

Referencia: JMX Tutorial, Cap. 3

 2
Author: Sane Dog,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-23 18:21:05

Si ejecuta su aplicación dentro del servidor de aplicaciones Glassfish, simplemente ejecute el siguiente comando asadmin, deberá reiniciar todos los servidores en ejecución para que el cambio afecte.

./asadmin enable-secure-admin

Hay configuraciones adicionales del servidor Glassfish para habilitar aún más la seguridad, vea más en Conectándose remotamente a Glassfish a través de JMX.

 0
Author: Yu Chen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-20 22:48:32