webentwicklung-frage-antwort-db.com.de

Öffnet Java 6 einen Standardport für JMX-Remoteverbindungen?

Meine spezifische Frage hat mit JMX zu tun, wie es in JDK 1.6 verwendet wird: wenn ich einen Java-Prozess mit JRE 1.6 mit verwende 

com.Sun.management.jmxremote

wählt Java in der Befehlszeile einen Standardport für Remote-JMX-Verbindungen aus?

Hintergrundgeschichte: Ich versuche derzeit, ein Verfahren zu entwickeln, das einem Kunden zur Verfügung gestellt wird, damit er über JMX von einem entfernten Rechner aus mit einem unserer Prozesse verbunden werden kann. Das Ziel besteht darin, das Remote-Debuggen einer auf einer Echtzeit-Anzeigekonsole auftretenden Situation zu erleichtern. Aufgrund ihrer Service-Level-Vereinbarung sind sie stark motiviert, so viele Daten wie möglich zu erfassen, und wenn die Situation zu kompliziert erscheint, um sie schnell zu beheben, müssen Sie die Anzeigekonsole neu starten und eine erneute Verbindung mit dem Server zulassen.

Ich bin mir bewusst, dass ich jconsole auf JDK 1.6-Prozessen und jvisualvm auf Post-JDK 1.6.7-Prozessen ausführen kann, wenn der Zugriff auf die Konsole physisch erfolgt. Aufgrund der betrieblichen Anforderungen und der damit verbundenen persönlichen Probleme sind wir jedoch sehr motiviert, die Daten, die wir benötigen, aus der Ferne zu übernehmen und wieder betriebsbereit zu machen.

BEARBEITEN: Ich kenne die Befehlszeilenport-Eigenschaft 

com.Sun.management.jmxremote.port=portNum

Die Frage, die ich zu beantworten versuche, lautet: Wenn Sie diese Eigenschaft nicht in der Befehlszeile festlegen, wählt Java einen anderen Port für die Fernüberwachung aus? Wenn ja, wie könnten Sie feststellen, was es sein könnte?

28
Bob Cross

Die documentation legt nahe, dass der JMX-Agent einen local - Port verwendet, der von außerhalb des Computers nicht erreichbar ist - es sei denn, Sie geben die folgende Eigenschaft an:

com.Sun.management.jmxremote.port=portNum

Dies geschieht aus Sicherheitsgründen sowie aus Gründen, die Herr Potato Head angegeben hat. Es sieht also so aus, als würde Java 6 für JMX keinen Standard-Port fernzugänglich öffnen.

BEARBEITEN: Hinzugefügt, nachdem das OP eine Antwort mit weiteren Informationen hinzugefügt hat.

Sie haben auch die Möglichkeit, einen lokalen Proxy zu erstellen, der alle lokalen JMX-Verbindungen überwacht und diese Informationen exportiert. Auf diese Weise brauchen Sie keine magische Konfiguration für jede JVM-Instanz auf dem Server. Stattdessen kann der lokale Proxy über JMX eine Verbindung zu allen JVMs herstellen und diese Informationen dann irgendwie aus der Ferne verfügbar machen. Ich bin nicht sicher, wie Sie dies implementieren würden, aber so etwas ist möglicherweise weniger Arbeit als das, was Sie sonst tun müssen, um alle Ihre JVMs per Fernzugriff über JMX bereitzustellen.

38
Eddie

AFAIK,

Hier sind die Möglichkeiten zum Verbinden eines JMX-Clientprozesses (einer Verwaltungsanwendung wie jconsole, jmxterm, mc4j, jvmstat, jmxmonitor, jps, ...) mit einem JMX-Serverprozess (der agent).

Es wird angenommen, dass das Protokoll, das den JMX-Client und den JMX-Server verbindet, 'Java RMI' (alias 'RMI-JRMP') ist. Dies sollte die Standardeinstellung sein. Man kann andere Protokolle konfigurieren, insbesondere 'RMI-IIOP' und 'JMXMP'. Spezielle Protokolle sind möglich: Das Projekt MX4J stellt beispielsweise zusätzlich SOAP/HTTP und verschiedene Serialisierungsprotokolle über HTTP bereit. 

Weitere Informationen zur Konfiguration finden Sie unter Sun/Oracle docs .

Schauen Sie sich auch die Datei jre/lib/management/management.properties in Ihrer JDK-Distribution an.

Also die Möglichkeiten:

Fall 0: Die JVM wird ohne bestimmte Konfiguration gestartet

Vor Java 6: Die JVM verhält sich nicht wie ein JMX-Server. Jedes Programm, das in der JVM ausgeführt wird, kann programmgesteuert auf die MBeanServer der JVM zugreifen und es für einen interessanten Datenaustausch zwischen Threads oder für die JVM-Überwachung verwenden, jedoch ist keine Verwaltung außerhalb des JVM-Prozesses möglich.

Seit Java 6: Auch wenn es nicht explizit konfiguriert ist, kann auf die JMX-Funktionalität der JVM lokal (von derselben Maschine) aus zugegriffen werden, wie in "Fall 1" beschrieben.

Fall 1: Die JVM wird mit -Dcom.Sun.management.jmxremote gestartet. 

Die JVM ist so konfiguriert, dass sie als JMX-Server mit local (nur für dieselbe Maschine) funktioniert.

In diesem Fall (und im Prinzip nur für Sun/Oracle-JVMs) kann ein JMX-Client über durch Speicher zugeordnete Dateien in /tmp/hsperfdata_[user] eine Verbindung zum JMX-Server herstellen. Darauf wird in der Sun-Dokumentation hingewiesen und als "lokales Monitoring" (und auch als Attach API ) bezeichnet. Es funktioniert nicht in FAT-Dateisystemen, da Berechtigungen dort nicht korrekt festgelegt werden können. Siehe dieser Blogeintrag .

Sun empfiehlt, jconsole auf einer vom JMX-Server getrennten Maschine auszuführen, da jconsole anscheinend ein Ressourcenschwund ist. Daher ist diese "lokale Überwachung" nicht unbedingt eine gute Idee.

Die lokale Überwachung ist jedoch ziemlich sicher, da sie nur lokal verwendet werden kann und leicht über Dateisystemberechtigungen gesteuert werden kann.

Fall 2: Der JMX-Server wird mit -Dcom.Sun.management.jmxremote.port=[rmiregistryport] gestartet. 

Die JVM ist so konfiguriert, dass sie als JMX-Server arbeitet, der an mehreren TCP - Ports überwacht wird.

Der in der Befehlszeile angegebene Port wird von der JVM zugewiesen und dort steht eine RMI-Registry zur Verfügung. Die Registry kündigt einen Connector mit dem Namen 'jmxrmi' an. Sie zeigt auf einen zweiten, zufällig zugewiesenen TCP - Port (einen "kurzlebigen" Port), an dem der JMX-RMI-Server zuhört und über den der tatsächliche Datenaustausch stattfindet.

Local ist wie in 'Fall 1' beschrieben immer in 'Fall 2' aktiviert.

Der JMX-Server überwacht standardmäßig alle Schnittstellen, sodass Sie eine Verbindung herstellen können (und ihn steuern können), indem Sie lokal eine Verbindung zu 127.0.0.1: [rmereegistryport] herstellen, und indem Sie eine Remote-Verbindung mit [einer beliebigen externen IP-Adresse] herstellen: [Einige Ports] . 

Dies bedeutet, dass Sie die Sicherheitsauswirkungen betrachten müssen. Sie können die JVM nur bei 127.0.0.1::rm_Registrationsport] anhören, indem Sie -Dcom.Sun.management.jmxremote.local.only=true einstellen.

Es ist eher unglücklich, dass man nicht angeben kann, wo der kurzlebige Port zugewiesen wird - er wird beim Start immer zufällig ausgewählt. Das kann bedeuten, dass Ihre Firewall zum Schweizer Käse der Verdammten werden muss! Es gibt jedoch Problemumgehungen . Insbesondere legt Apache Tomcat den temporären JMX-RMI-Serverport über seinen JMX Remote Lifecycle Listener fest. Den Code für diese kleine Magie finden Sie unter org.Apache.catalina.mbeans.JmxRemoteLifecycleListener .

Wenn Sie diese Methode verwenden, können Sie auch Folgendes sicherstellen:

  1. Der JMX-Client muss sich beim JMX-Server authentifizieren
  2. Der TCP -Austausch zwischen Client und Server wird mit SSL verschlüsselt

Wie das gemacht wird, ist in der Sun/Oracle-Dokumentation beschrieben.

Andere Ansätze

Sie können interessante Permutationen ausführen, um zu vermeiden, dass Sie das RMI-Protokoll verwenden müssen. Insbesondere können Sie Ihrem Prozess eine Servlet-Engine (wie Jetty) hinzufügen. Fügen Sie dann Servlets hinzu, die einen gewissen HTTP-basierten Austausch intern in direkte Zugriffe auf die MBeanServer der JVM übersetzen. Sie befinden sich dann in "Fall 0", verfügen jedoch weiterhin über Verwaltungsfunktionen, möglicherweise über eine HTML-basierte Schnittstelle. Die JBoss JMX Console ist ein Beispiel dafür.

Neben dem Thema könnten Sie SNMP direkt verwenden (etwas, das ich nicht ausprobiert habe), entsprechend dieses Dokument .

Show and Tell Time 

Und jetzt ist es Zeit für Code, um einen JXM-Austausch zu veranschaulichen. Wir lassen uns von einem Sunoracle-Tutorial inspirieren.

Dies läuft unter Unix. Wir verwenden eine JVM, die als JMX-Server konfiguriert ist.

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

Wir verwenden lsof, um zu überprüfen, welche TCP Ports offen gehalten werden:

lsof -p <processid> -n | grep TCP

Man sollte etwas wie den Registrierungsport und den kurzlebigen Port sehen:

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

Wir verwenden tcpdump, um den Paketaustausch zwischen JMX-Client und JMX-Server zu überprüfen:

tcpdump -l -XX port 36828 or port 9001

Wir richten im Home-Verzeichnis eine Datei .Java.policy ein, damit der Client tatsächlich eine Remote-Verbindung herstellen kann:

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

Und dann können wir das ausführen und sehen, was passiert:

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
        //
    }   
}
87
David Tonhofer

Tatsächlich gibt es eine undokumentierte Eigenschaft, die Sie verwenden können, um zu erzwingen, dass JMX aus der Ferne zugreifbare Connectors für zufällige Portnummern erstellt.

-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"

Die letzten beiden Eigenschaften sind von größter Bedeutung.

6
Ivan Koblik

Die documentation scheint darauf hinzuweisen, dass der JMX-Agent einen lokalen temporären Port verwendet, sofern nicht Sie geben die folgende Eigenschaft an:

com.Sun.management.jmxremote.port=portNum

Standardports werden vermieden, da auf einem System viele Java-Anwendungen vorhanden sein könnten, und wenn ein Standardport vorhanden ist, kann nur eine Anwendung verwaltet werden! Die obige Konfigurationseigenschaft wird für die Verwaltung von ausdrücklicher Zweck von remote bereitgestellt.

Wenn Sie darauf bestehen müssen, einen kurzlebigen Port zu verwenden, sollte die URL des JMX-Agenten in der JVM über die folgende Systemeigenschaft zugänglich sein (obwohl dies wahrscheinlich eine lokale Adresse ist):

com.Sun.management.jmxremote.localConnectorAddress

Hinweis: Ich denke, Sie könnten immer einen Socket für eine remote verfügbare Adresse und Proxy-Anfragen auf den lokalen Socket öffnen, aber die Verwendung der verfügbaren Option scheint viel attraktiver zu sein!

3
David Grant

Die kurze Antwort auf meine Frage lautet also "Nein".

Es ist jedoch interessant zu untersuchen, warum. Sehen Sie sich die netstat-Ausgabe einer gültigen lokalen Verbindung an. Hier sind die Ports, die ich aufgrund einer jconsole, die eine lokale Verbindung zu sich selbst herstellt, geöffnet sehen. Wie Sie sehen, ist Port 1650 der lokale Port, der für die JMX-Informationen verwendet wird: 

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

Es reicht jedoch nicht aus, zu versuchen, jconsole mit localhost:1650 zu verbinden. Leider erhalten Sie nur eine Meldung "Verbindung fehlgeschlagen: kein Objekt in der Tabelle".

Die Schlussfolgerung meiner ursprünglichen Geschichte lautet daher: Wenn wir die Fernüberwachung mit JMX für unsere Kunden vereinfachen möchten, müssen wir wirklich eindeutige individuelle Fernzugriffsports für die verschiedenen Java-Prozesse identifizieren, die in unserem System gestartet werden. Glücklicherweise ist dies alles die vernünftige Verwendung des Arguments VM:

com.Sun.management.jmxremote.port=portNum

dort haben wir fast sicher einen sequentiellen vordefinierten Bereich von portNum, so dass der Kunde anhand der Portnummer die richtige Remote-Anwendung auswählen kann.

2
Bob Cross

Ich habe in letzter Zeit daran gearbeitet, herauszufinden, wie die Remote-JMX-Verwaltung aus Java-Code heraus aktiviert werden kann, ohne dass die JVM mit speziellen Eigenschaften gestartet werden muss. Die Lösung, die ich beschlossen habe, ist, meine eigene private RMI-Registry zu erstellen - ganz einfach - und den JMX-Dienst in dieser Registry verfügbar zu machen. Ich erstelle meinen eigenen MBeanServer und erstelle dann einen neuen JMXConnectorServer. Der JMXConnectorServer wird durch einen Aufruf wie erstellt

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

Dabei ist Server der MBeanServer und URL eine Instanz von JMXServiceURL.

Die URL hat die Form "service: jmx: rmi: /// jndi/rmi: // localhost:/jmxrmi", wobei port die Portnummer der (lokalen) privaten Registrierung ist. "jmxrmi" ist der Standard-Dienstname für den JMX-Dienst.

Nachdem ich dies eingerichtet und den Connector gestartet habe, kann ich von jconsole aus mithilfe von Hostname: Port eine Verbindung herstellen.

Dies entspricht ganz meinen Bedürfnissen. Ich würde gerne wissen, ob jemand bei diesem Ansatz einen Fehler sieht.

Referenz: JMX Tutorial, Kap. 3

2
Sane Dog

Wenn Sie Ihre Anwendung innerhalb des Glassfish App-Servers ausführen, führen Sie einfach den folgenden asadmin-Befehl aus. Sie müssen alle laufenden Server neu starten, damit die Änderung wirksam wird. 

./asadmin enable-secure-admin

Es gibt zusätzliche Glassfish-Serverkonfigurationen, um die Sicherheit weiter zu verbessern. Weitere Informationen finden Sie unter Remote-Verbindung zu Glassfish über JMX .

0
Yu Chen