Java 6 ouvre-t-il un port par défaut pour les connexions à distance JMX?
ma question spécifique concerne JMX tel qu'il est utilisé dans JDK 1.6: Si j'exécute un processus Java en utilisant JRE 1.6 avec
com.sun.management.jmxremote
dans la ligne de commande, est-ce que Java choisit un port par défaut pour les connexions JMX distantes?
Backstory: j'essaie actuellement de développer une procédure à donner à un client qui lui permettra de se connecter à l'un de nos processus via JMX à partir d'une machine distante. Le but est de faciliter leur débogage à distance d'une situation se produisant sur un affichage en temps réel de la console. En raison de leur accord sur le niveau de service, ils sont fortement motivés à saisir le plus de données possible et, si la situation semble trop compliquée pour être corrigée rapidement, à redémarrer la console d'affichage et à lui permettre de se reconnecter au côté serveur.
je suis conscient que je pourrais courir jconsole sur JDK 1.6 processus et jvisualvm sur post-JDK 1.6.7 processus ayant un accès physique à la console. Toutefois, en raison de l'exploitation de la exigences et Ressources Humaines problèmes en jeu, Nous sommes fortement motivés à saisir les données dont nous avons besoin à distance et à les remettre en service.
EDIT: je suis conscient de la ligne de commande port de la propriété
com.sun.management.jmxremote.port=portNum
la question à laquelle j'essaie de répondre est, si vous ne définissez pas cette propriété sur la ligne de commande, est-ce que Java choisit un autre port pour la surveillance à distance? Si oui, comment pourriez-vous déterminer ce que cela pourrait être?
7 réponses
documentation suggère que L'agent JMX utilise un port -- quelque chose d'inaccessible de l'extérieur de la machine -- sauf si vous spécifiez la propriété suivante:
com.sun.management.jmxremote.port=portNum
C'est pour des raisons de sécurité, ainsi que pour la raison donnée par M. Patate. Par conséquent, il semble que Java 6 n'ouvre pas par défaut accessible à distance port pour JMX.
EDIT: ajouté après L'OP ajouté une réponse avec plus information.
une autre option que vous avez est de créer d'une manière ou d'une autre un mandataire local qui écoute toutes les connexions JMX locales et exporte cette information. De cette façon, vous n'avez pas besoin d'avoir une telle configuration magique de chaque instance JVM sur le serveur. Au lieu de cela, le mandataire local peut se connecter à toutes les JVM via JMX et ensuite exposer ces informations à distance. Je ne suis pas certain de la façon dont vous mettriez cela en œuvre, mais quelque chose comme ça pourrait être moins de travail que ce que vous devez faire autrement. exposer tous vos JVM à distance via JMX.
autant que je sache,
Voici les possibilités pour connecter un client JMX processus (application de gestion comme jconsole, jmxterm, mc4j, jvmstat, jmxmonitor, jps,...) pour un processus du serveur JMX ( agent).
le protocole de connexion entre le client JMX et le serveur JMX est supposé être' Java RMI '(alias'RMI-JRMP'). Ce doit être la valeur par défaut. On peut configurer autres les protocoles, notamment "RMI-IIOP" et "JMXMP". Des protocoles spéciaux sont possibles: MX4J project par exemple fournit en plus SOAP/HTTP et divers protocoles de sérialisation sur HTTP.
reportez-vous à Sun / Oracle docs pour plus de détails sur la configuration.
consultez Également le fichier jre/lib/management/management.properties
dans votre distribution JDK.
alors, les possibilités:
Cas 0: la JVM est lancée sans toute configuration particulière
avant Java 6: la JVM ne se comporte pas comme un serveur JMX. Tout programme exécuté à l'intérieur de la JVM peut accéder aux MBeanServer mais aucune gestion en dehors du processus JVM n'est possible.
depuis Java 6: même s'il n'est pas explicitement configuré, on peut accéder aux fonctionnalités JMX de la JVM en local (à partir de la même machine) comme décrit dans le "Cas 1".
Cas 1: la JVM commence par -Dcom.sun.management.jmxremote
la JVM est configurée pour fonctionner comme un (de même machine seule) serveur JMX.
dans ce cas (et en principe seulement pour les JVMs Sun/Oracle), un client JMX peut se connecter au serveur JMX par le biais de fichiers mappés en mémoire trouvés dans /tmp/hsperfdata_[user]
. Cela est fait allusion dans la documentation Sun et appelé " local suivi " (et aussi le Attach API). Il ne fonctionne pas sur les systèmes de fichiers FAT car les permissions ne peuvent pas y être définies correctement. Voir cette entrée de blog.
le soleil recommande de courir jconsole
sur une machine séparée du serveur JMX jconsole
apparemment, c'est un porc-ressource, donc cette "surveillance locale" n'est pas nécessairement une bonne idée.
la surveillance locale est, cependant, assez sûre, n'étant utilisable que localement et étant facilement contrôlable par les permissions du système de fichiers.
cas 2: le serveur JMX démarre avec -Dcom.sun.management.jmxremote.port=[rmiregistryport]
le JVM est configuré pour fonctionner comme un serveur JMX écoutant sur plusieurs ports TCP.
le port spécifié sur la ligne de commande sera attribué par la JVM et un registre RMI y sera disponible. Le registre annonce un connecteur nommé "jmxrmi". Il pointe vers un second port TCP (un port "éphémère") sur lequel le JMX RMI le serveur écoute et à travers lequel l'échange de données réel a lieu.
Local comme décrit dans ' Case 1 'est toujours activé dans'Case 2'.
le serveur JMX écoute toutes les interfaces par défaut, vous pouvez donc vous y connecter (et le contrôler) en vous connectant localement à 127.0.0.1:[rmiregistryport] ainsi qu'en vous connectant à distance à [n'importe quelle adresse IP extérieure]:[quelque port] à distance.
cela implique que vous devez examiner les implications en matière de sécurité. Vous pouvez faire écouter JVM sur 127.0.0.1:[rmiregistryport] seulement en positionnant -Dcom.sun.management.jmxremote.local.only=true
.
il est plutôt regrettable qu'on ne puisse pas spécifier où le port éphémère sera attribué - il est toujours choisi au hasard au démarrage. Cela pourrait bien signifier que votre pare-feu doit devenir le fromage suisse des damnés! Cependant, il y a solutions. En particulier, Apache Tomcat configure l'éphémère port serveur JMX RMI via son JMX Remote Lifecycle Écouteur. Le code pour effectuer cette petite magie peut être trouvé à org.Apache.Catalina.mbeans.JmxRemoteLifecycleListener.
si vous utilisez cette méthode, assurez-vous que:
- le client JMX doit s'authentifier sur le serveur JMX
- l'échange TCP entre le client et le serveur est crypté en utilisant SSL
Comment faire cela est décrit dans le soleil / Oracle documentation
d'Autres approches
vous pouvez faire des permutations intéressantes pour éviter d'avoir à utiliser le protocole RMI. En particulier, vous pouvez ajouter un moteur servlet (comme Jetty) à votre processus. Ensuite, ajoutez des servlets qui convertissent en interne certains échanges HTTP en accès directs auxMBeanServer
. Vous seriez alors dans 'case 0' mais avez toujours des capacités de gestion, peut-être par l'intermédiaire d'une interface HTML. JBoss JMX Console en est un exemple.
plus hors-sujet, vous pouvez utiliser SNMP directement (quelque chose que je n'ai pas essayé) selon ce document.
Montrer et Dire le Temps
Et maintenant il est temps pour un certain code d'illustrer un échange JXM. Nous nous inspirons de un tutoriel Sunoracle.
ceci fonctionne sur Unix. Nous utilisons une JVM configurée comme un serveur JMX en utilisant:
-Dcom.sun.management.jmxremote.port=9001
Nous utilisons lsof
pour vérifier quels ports TCP sont ouverts:
lsof -p <processid> -n | grep TCP
on devrait voir quelque chose comme ceci, le port de registre et le port éphémère:
java 1068 user 127u IPv6 125614246 TCP *:36828 (LISTEN)
java 1068 user 130u IPv6 125614248 TCP *:9001 (LISTEN)
Nous utilisons tcpdump
pour inspecter l'échange de paquets entre le client JMX et le serveur JMX:
tcpdump -l -XX port 36828 or port 9001
nous avons créé un fichier .java.policy
dans le répertoire d'accueil pour permettre au client de se connecter à distance:
grant {
permission java.net.SocketPermission
"<JMX server IP address>:1024-65535", "connect,resolve";
};
Et puis nous pouvons le lancer et voir ce qui se passe:
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
//
}
}
en fait il y a une propriété non documentée que vous pouvez utiliser pour forcer JMX à créer des connecteurs accessibles à distance sur des numéros de port aléatoires.
-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"
Les deux dernières propriétés sont les plus importantes.
documentation semble indiquer que L'agent JMX utilise un port local éphémère, à moins que vous spécifiez la propriété suivante:
com.sun.management.jmxremote.port=portNum
les ports par défaut sont évités car vous pourriez avoir beaucoup applications java sur un système, et s'il y avait un port par défaut, une seule application pourrait être gérée! La propriété de configuration ci-dessus est fournie pour le butdistance gestion.
si vous devez insister pour utiliser un port éphémère, alors L'URL de L'agent JMX doit être accessible depuis la JVM, via la propriété système suivante (bien qu'il s'agisse probablement d'une adresse locale):
com.sun.management.jmxremote.localConnectorAddress
Remarque:: je suppose que vous pouvez toujours ouvrir une socket sur une adresse disponible à distance et des requêtes proxy sur la socket locale, mais utiliser l'option disponible semble beaucoup plus attrayant!
Donc, la réponse à ma question est "non."
Toutefois, il est intéressant d'examiner pourquoi. Regarder les netstat
sortie d'une connexion locale valide. Voici les ports que je vois s'ouvrir suite à un jconsole
établir une connexion locale avec elle-même. Comme vous pouvez le voir, le port 1650 est le port local utilisé pour les informations 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
Toutefois, il ne suffit pas d'essayer de se connecter jconsole
localhost:1650
. Malheureusement, tout ce qui va vous filet est un "Échec de la connexion: aucun objet de ce type dans le message table".
donc, la conclusion de mon histoire originale est que, si nous voulons faciliter la surveillance à distance en utilisant JMX pour nos clients, nous devons vraiment identifier les ports d'accès à distance uniques pour la variété de processus Java qui sont lancés dans notre système. Heureusement, tout ce que cela exige est l'utilisation judicieuse de l'argument de la VM:
com.sun.management.jmxremote.port=portNum
où nous aurons presque certainement une gamme séquentielle prédéfinie portNum
pour que le client puisse sélectionner la bonne application distante en utilisant le numéro de port.
j'ai travaillé récemment pour comprendre comment activer la gestion JMX à distance à partir du code java, sans exiger que la JVM ait été lancée avec des propriétés spéciales définies. La solution que j'ai choisie est de lancer mon propre registre RMI privé -- assez facile -- et d'exposer le service JMX sur ce registre. Je crée mon propre MBeanServer, puis je crée un nouveau JMXConnectorServer. Le JMXConnectorServer est créé par un appel comme
connector = JXMConnectorServerFactory.newJMXConnectorServer(url, null, server);
où le serveur est le MBeanServer, et url est une instance de JMXServiceURL.
l'url est de la forme "service: jmx:rmi://jndi/rmi:/ / localhost: / jmxrmi" où le port est le numéro de port du registre privé (local). "jmxrmi" est le nom de service standard du service JMX.
après avoir configuré ceci, et démarré le connecteur, je trouve que je peux me connecter depuis jconsole en utilisant hostname:port.
C'entièrement adresses mes besoins, je serai intéressé de savoir si quelqu'un voit une faille dans cette approche.
Référence: tutoriel JMX, Chap. 3
si vous exécutez votre application à L'intérieur de Glassfish app server, lancez simplement la commande asadmin suivante, vous devrez redémarrer tous les serveurs en cours d'exécution pour que le changement prenne effet.
./asadmin enable-secure-admin
il y a des configurations supplémentaires de serveur Glassfish pour activer davantage la sécurité, voir plus à Connecting remote to Glassfish through JMX.