代码之家  ›  专栏  ›  技术社区  ›  Bob Cross n8wrl

Java 6是否为JMX远程连接打开默认端口?

  •  28
  • Bob Cross n8wrl  · 技术社区  · 16 年前

    我的特定问题与JDX中使用的JMX有关:如果我正在使用JRE 1.6运行Java进程

    com.sun.management.jmxremote
    

    在命令行中,Java是否为远程JMX连接选择默认端口?

    后台:我目前正在尝试开发一个过程,以提供给客户,使他们能够通过JMX从远程机器连接到我们的一个进程。其目标是方便远程调试实时显示控制台上发生的情况。由于他们的服务级别协议,他们有强烈的动机去捕获尽可能多的数据,如果情况看起来太复杂而无法快速修复,他们可以重新启动显示控制台并允许它重新连接到服务器端。

    我知道我可以跑 jconsole 在JDK 1.6过程和 jvisualvm 在JDK 1.6.7之后的进程中,允许物理访问控制台。但是,由于操作要求和涉及的人员问题,我们强烈希望能够远程获取所需的数据,并让它们重新启动和运行。

    编辑:我知道命令行端口属性

    com.sun.management.jmxremote.port=portNum
    

    我试图回答的问题是,如果您不在命令行中设置该属性,Java是否选择另一个端口进行远程监控?如果是这样,你怎么能确定它可能是什么?

    7 回复  |  直到 10 年前
        1
  •  38
  •   Eddie    16 年前

    这个 documentation 建议JMX代理使用 地方的 端口--从计算机外部无法访问的内容--除非指定以下属性:

    com.sun.management.jmxremote.port=portNum

    这是出于安全考虑,也是因为土豆头先生给出的理由。因此,看起来Java 6没有打开默认值。 远程访问 JMX的端口。

    编辑:在OP添加包含更多信息的答案后添加。

    另一个选择是以某种方式创建一个本地代理,它监听所有本地JMX连接并导出这些信息。这样,您就不需要对服务器上的每个JVM实例进行这种神奇的配置。相反,本地代理可以通过JMX连接到所有JVM,然后以某种方式远程公开这些信息。我不是很肯定您将如何实现这一点,但是像这样的事情可能比您通过JMX远程公开所有JVM所必须做的工作要少。

        2
  •  87
  •   Esko Luontola    13 年前

    嗯,

    以下是连接 JMX客户端进程 (A) 管理应用程序 像jconsole、jmxterm、mc4j、jvmstat、jmxmonitor、jps等)到 JMX服务器进程 (the 代理人 )

    连接JMX客户端和JMX服务器的协议被假定为“Java RMI”(AKA’RMI-JRMP’)。这应该是默认设置。一个人可以配置 other protocols 尤其是“RMI-IIOP”和“JMXMP”。可以使用特殊协议: MX4J 例如,Project还通过HTTP提供SOAP/HTTP和各种序列化协议。

    参照 Sun/Oracle docs 有关配置的详细信息。

    还要看一下文件 jre/lib/management/management.properties 在您的JDK发行版中。

    所以,可能性是:

    案例0:JVM在没有任何特定配置的情况下启动

    在Java 6之前:JVM不作为JMX服务器来运行。在JVM内部运行的任何程序都可以访问JVM的 MBeanServer 以编程方式进行,并使用它在线程之间进行有趣的数据交换或进行JVM监视,但不可能从JVM进程外部进行管理。

    由于Java 6:即使不显式配置,也可以访问JVM的JMX功能。 局部地 (来自同一台机器)如“案例1”所述。

    案例1:JVM是以 -Dcom.sun.management.jmxremote

    JVM被配置为 地方的 (仅限同一台计算机)JMX服务器。

    在这种情况下(原则上仅适用于Sun/Oracle JVM),JMX客户机可以通过中的内存映射文件连接到JMX服务器。 /tmp/hsperfdata_[user] . 这在Sun文档中有所提及,称为“本地监控”(以及 Attach API )它不能在FAT文件系统上工作,因为无法在那里正确设置权限。见 this blog entry .

    Sun建议跑步 jconsole 在与JMX服务器分离的计算机上 J控制台 显然是一个资源霸主,所以这个“本地监控”的事情不一定是一个好主意。

    但是,本地监控相当安全,只在本地可用,并且可以通过文件系统权限轻松控制。

    案例2:JMX服务器是以 -Dcom.sun.management.jmxremote.port=[rmiregistryport]

    JVM被配置为作为一个JMX服务器在多个TCP端口上侦听。

    命令行上指定的端口将由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 .

    不幸的是,我们不能指定临时端口的分配位置——它总是在启动时随机选择。这很可能意味着你的防火墙需要成为该死的瑞士奶酪!但是,有 workarounds . 尤其是,ApacheTomcat通过其 JMX Remote Lifecycle Listener . 执行这个小魔术的代码可以在 org.apache.catalina.mbeans.JmxRemoteLifecycleListener .

    如果使用此方法,还可以确保:

    1. JMX客户机必须通过JMX服务器的身份验证
    2. 客户端和服务器之间的TCP交换使用SSL加密

    如何做到这一点在 the Sun/Oracle documentation

    其他方法

    您可以进行有趣的排列以避免使用RMI协议。特别是,您可以将servlet引擎(如jetty)添加到您的流程中。然后添加servlet,将一些基于HTTP的交换内部转换为对JVM的直接访问 MBeanServer . 然后,您将处于“案例0”中,但仍然具有管理功能,可能是通过基于HTML的接口实现的。这个 JBoss JMX Console 就是一个例子。

    更离题的是,根据 this document .

    显示和显示时间

    现在是时候用一些代码来说明JXM交换了。我们的灵感来自 a Sunoracle tutorial .

    这在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
            //
        }   
    }
    
        3
  •  6
  •   Ivan Koblik    13 年前

    实际上,有一个未记录的属性可以用于强制JMX在随机端口号上创建可远程访问的连接器。

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

    最后两个属性是最重要的。

        4
  •  3
  •   community wiki 4 revs David Grant    16 年前

    这个 documentation 似乎表明JMX代理使用本地临时端口, 除非 指定以下属性:

    com.sun.management.jmxremote.port=portNum
    

    默认端口被避免,因为您可以 许多的 Java应用程序在一个系统上,如果有缺省端口,只有一个应用程序能够被管理!上面的配置属性是为 明确目的 属于 遥远的 管理。

    如果您必须坚持使用临时端口,那么JMX代理的URL应该可以通过以下系统属性从JVM中访问(尽管这可能是本地地址):

    com.sun.management.jmxremote.localConnectorAddress
    

    注释 :我想您总是可以在远程可用地址上打开一个套接字,在本地套接字上打开代理请求,但是使用可用选项似乎更有吸引力!

        5
  •  2
  •   Bob Cross n8wrl    16 年前

    所以,我问题的简短答案是“不”。

    然而,研究原因是很有趣的。看看 netstat 从有效的本地连接输出。以下是由于 jconsole 与自身建立本地连接。如您所见,端口1650是用于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
    

    但是,尝试连接还不够 J控制台 localhost:1650 . 遗憾的是,所有能让您结网的都是一条“连接失败:表中没有这样的对象”消息。

    因此,我的故事的结论是,如果我们将促进使用JMX对客户进行远程监控,我们确实需要为我们系统中启动的各种Java进程识别唯一的单个远程访问端口。幸运的是,所有这些都需要明智地使用vm参数:

    com.sun.management.jmxremote.port=portNum
    

    我们几乎肯定会有一个预先指定的连续范围 portNum 以便客户可以使用端口号选择正确的远程应用程序。

        6
  •  2
  •   Sane Dog    13 年前

    我最近一直在研究如何从Java代码中启用远程JMX管理,而不需要JVM以特殊的属性集启动。我解决的解决方案是启动我自己的私有RMI注册表——足够简单——并在该注册表上公开JMX服务。我创建自己的MBeanServer,然后创建一个新的JMxConnectorServer。jmxConnectorServer是通过如下调用创建的

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

    其中server是MBeanServer,url是jmxServiceURL的实例。

    URL的格式为“service:jmx:rmi:///jndi/rmi://localhost:/jmx rmi”,其中port是(本地)私有注册表的端口号。jmxrmi“是jmx服务的标准服务名。

    在设置好这个接口并启动连接器之后,我发现可以使用hostname:port从jconsole连接到它。

    这完全满足了我的需要;我有兴趣知道是否有人看到这种方法的缺陷。

    参考文献: JMX Tutorial, Chap. 3

        7
  •  0
  •   BJYC    10 年前

    如果您碰巧在glassfish app server中运行应用程序,只需运行以下asadmin命令,就需要重新启动所有正在运行的服务器,更改才会生效。

    /asadmin启用安全管理

    有额外的glassfish服务器配置来进一步启用安全性,请参阅 Connecting remotely to Glassfish through JMX .