阿法伊克,
以下是将 JMX 客户端进程(像 jconsole、jmxterm、mc4j、jvmstat、jmxmonitor、jps 等管理应用程序)连接到 JMX 服务器进程(代理)的可能性。
连接 JMX 客户端和 JMX 服务器的协议假定为“Java RMI”(又名“RMI-JRMP”)。这应该是默认设置。可以配置其他协议,特别是“RMI-IIOP”和“JMXMP”。特殊协议是可能的:例如,MX4J项目还通过HTTP提供SOAP / HTTP和各种序列化协议。
有关配置的详细信息,请参阅 Sun/Oracle 文档。
还可以查看 JDK 发行版中的文件。jre/lib/management/management.properties
因此,可能性:
情况 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 客户机可以通过 中的内存映射文件连接到 JMX 服务器。这在 Sun 文档中有所提及,称为“本地监视”(以及 Attach API)。它不适用于 FAT 文件系统,因为无法在此处正确设置权限。请参阅此博客文章。/tmp/hsperfdata_[user]
Sun建议在与JMX服务器分开的机器上运行,因为显然是一个资源浪费,所以这种“本地监控”的事情不一定是一个好主意。jconsole
jconsole
但是,本地监视相当安全,只能在本地使用,并且可以通过文件系统权限轻松控制。
案例 2:JMX 服务器以 -Dcom.sun.management.jmxremote.port=[rmiregistryport]
启动
JVM 配置为作为侦听多个 TCP 端口的 JMX 服务器工作。
命令行上指定的端口将由 JVM 分配,并且 RMI 注册表将在那里可用。注册表播发名为“jmxrmi”的连接器。它指向第二个随机分配的 TCP 端口(一个“临时”端口),JMX RMI 服务器在其上侦听并通过该端口进行实际数据交换。
“案例 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远程生命周期监听器设置了临时的JMX RMI服务器端口。执行这个小魔术的代码可以在org.apache.catalina.mbeans.JmxRemoteLifecycleListener上找到。
如果使用此方法,则最好确保:
- JMX 客户机必须向 JMX 服务器进行身份验证
- 客户端和服务器之间的 TCP 交换使用 SSL 进行加密
如何做到这一点在 Sun/Oracle 文档中进行了描述
其他方法
您可以执行有趣的排列,以避免必须使用 RMI 协议。特别是,您可以在流程中添加一个 servlet 引擎(如 Jetty)。然后添加 servlet,这些服务将一些基于 HTTP 的交换在内部转换为对 JVM 的直接访问。然后,您将处于“情况0”中,但仍然具有管理功能,可能通过基于HTML的界面。JBoss JMX Console 就是一个例子。MBeanServer
更偏离主题,根据本文档,您可以直接使用SNMP(我还没有尝试过)。
显示和告诉时间
现在是时候用一些代码来说明JXM交换了。我们从 Sunoracle 教程中汲取灵感。
这运行在Unix上。我们使用配置为 JMX 服务器的 JVM,使用:
-Dcom.sun.management.jmxremote.port=9001
我们用来检查它打开的TCP端口:lsof
lsof -p <processid> -n | grep TCP
人们应该看到这样的东西,注册表端口和临时端口:
java 1068 user 127u IPv6 125614246 TCP *:36828 (LISTEN)
java 1068 user 130u IPv6 125614248 TCP *:9001 (LISTEN)
我们用于检查 JMX 客户端和 JMX 服务器之间的数据包交换:tcpdump
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
//
}
}