私の知る限り、
以下は、JMX クライアント プロセス( jconsole、jmxterm、mc4j、jvmstat、jmxmonitor、jps などの管理アプリケーション) をJMX サーバー プロセス(エージェント) に接続するための可能性です。
JMX クライアントと JMX サーバーを接続するプロトコルは、「Java RMI」(別名「RMI-JRMP」) であると想定されます。これがデフォルトです。他のプロトコル、特に「RMI-IIOP」と「JMXMP」を構成できます。特別なプロトコルが可能です。たとえば、MX4Jプロジェクトは、SOAP/HTTP および HTTP を介したさまざまなシリアライゼーション プロトコルを追加で提供します。
構成の詳細については、 Sun/Oracle のドキュメントを参照してください。
jre/lib/management/management.properties
また、JDK ディストリビューションのファイルも確認してください。
したがって、可能性:
ケース 0: JVM は特定の構成なしで開始されます
Java 6 より前: JVM は JMX サーバーとして動作しません。JVM 内で実行されるプログラムは、プログラムによって JVM のMBeanServerにアクセスし、それを使用してスレッド間の興味深いデータ交換や JVM の監視を行うことができますが、JVM プロセスの外部からの管理はできません。
Java 6 以降: 明示的に構成されていない場合でも、「ケース 1」で説明されているように、ローカルで(同じマシンから) JVM の JMX 機能にアクセスできます。
ケース 1: JVM は以下で開始されます-Dcom.sun.management.jmxremote
JVM は、ローカル(同一マシンのみ) JMX サーバーとして機能するように構成されています。
この場合 (原則として Sun/Oracle JVM のみ)、JMX クライアントは、/tmp/hsperfdata_[user]
. これは、Sun のドキュメントで言及されており、「ローカル監視」(およびAttach API ) と呼ばれています。パーミッションを正しく設定できないため、FAT ファイルシステムでは機能しません。このブログエントリを参照してください。
Sunjconsole
は、JMX サーバーとは別のマシンで実行することをお勧めします。JMX サーバーはjconsole
明らかにリソースを大量に消費するため、この「ローカル監視」は必ずしも良い考えではありません。
ただし、ローカル監視はかなり安全で、ローカルでのみ使用でき、ファイルシステムのアクセス許可によって簡単に制御できます.
ケース 2: JMX サーバーは以下で開始されます-Dcom.sun.management.jmxremote.port=[rmiregistryport]
JVM は、複数の TCP ポートをリッスンする JMX サーバーとして機能するように構成されています。
コマンド ラインで指定されたポートは JVM によって割り当てられ、そこで RMI レジストリが使用可能になります。レジストリは、「jmxrmi」という名前のコネクタをアドバタイズします。これは、JMX RMI サーバーがリッスンし、実際のデータ交換が行われる、ランダムに割り当てられた 2 番目の TCP ポート (「エフェメラル」ポート) を指します。
「ケース 1」で説明したローカルは、「ケース 2」で常に有効になります。
JMX サーバーはデフォルトですべてのインターフェースをリッスンするため、ローカルで 127.0.0.1:[rmiregistryport] に接続するだけでなく、[任意の外部 IP アドレス]:[一部のポート] にリモートで接続して接続 (および制御) できます。 .
これは、セキュリティへの影響を検討する必要があることを意味します。を設定するだけで、JVM が 127.0.0.1:[rmiregistryport] をリッスンできます-Dcom.sun.management.jmxremote.local.only=true
。
エフェメラル ポートがどこに割り当てられるかを指定できないのはかなり残念なことです。起動時に常にランダムに選択されます。これは、ファイアーウォールがスイスチーズのようになる必要があることを意味しているのかもしれません! ただし、回避策があります。特に、Apache Tomcat は、 JMX Remote Lifecycle Listenerを介してエフェメラル JMX RMI サーバー ポートを設定します。この小さな魔法を実行するコードは、org.apache.catalina.mbeans.JmxRemoteLifecycleListenerにあります。
この方法を使用する場合は、次のことを確認してください。
- JMX クライアントは JMX サーバーに対して認証する必要があります
- クライアントとサーバー間の TCP 交換は、SSL を使用して暗号化されます。
その方法については、Sun/Oracle のドキュメントに記載されています。
その他のアプローチ
RMI プロトコルを使用する必要がないように、興味深い順列を行うことができます。特に、サーブレット エンジン (Jetty など) をプロセスに追加できます。次に、一部の HTTP ベースの交換を内部的に JVM への直接アクセスに変換するサーブレットを追加しますMBeanServer
。その後、「ケース 0」になりますが、おそらく HTML ベースのインターフェイスを介して管理機能を利用できます。JBoss JMX コンソールはこの例です。
このドキュメントに従って、SNMPを直接使用することもできます(私が試したことはありません)。
時間の表示と通知
それでは、コードで JXM 交換を説明します。Sunoracle のチュートリアルから着想を得ています。
これは Unix で動作します。以下を使用して JMX サーバーとして構成された JVM を使用します。
-Dcom.sun.management.jmxremote.port=9001
lsof
開いている TCP ポートを確認するために使用します。
lsof -p <processid> -n | grep TCP
次のようなレジストリ ポートとエフェメラル ポートが表示されるはずです。
java 1068 user 127u IPv6 125614246 TCP *:36828 (LISTEN)
java 1068 user 130u IPv6 125614248 TCP *:9001 (LISTEN)
tcpdump
JMX クライアントと JMX サーバー間のパケット交換を検査するために使用します。
tcpdump -l -XX port 36828 or port 9001
.java.policy
クライアントが実際にリモートで接続できるように、ホーム ディレクトリにファイルを設定します。
grant {
permission java.net.SocketPermission
"<JMX server IP address>:1024-65535", "connect,resolve";
};
そして、これを実行して何が起こるか見ることができます:
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
//
}
}