当配置JMX时,为什么Java会打开3个端口?

72

我在Centos6上使用JDK7运行我的Java程序。我使用以下选项启用JMX:

JAVA_OPTS="${JAVA_OPTS} -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9123 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.local.only=true"

当我检查打开的端口时,我发现另外两个随机端口:

netstat -plunt | grep java
tcp        0      0 :::9123                     :::*                        LISTEN      13295/java
tcp        0      0 :::59927                    :::*                        LISTEN      13295/java
tcp        0      0 :::59928                    :::*                        LISTEN      13295/java
请注意,每次重启只有端口9123的配置保持不变,另外两个端口的值会发生变化。
netstat -plunt | grep java
tcp        0      0 :::9123                     :::*                        LISTEN      13331/java
tcp        0      0 :::59932                    :::*                        LISTEN      13331/java
tcp        0      0 :::59933                    :::*                        LISTEN      13331/java

什么是2个附加端口,为什么它们被打开?

如何配置2个随机的附加端口?

如何配置::ffff:127.0.0.1出现在JMX打开的所有端口之前?

为什么连接JConsole时没有使用一个端口?

添加以澄清答案

不幸的是,额外的随机端口仍然是打开状态 提醒一下,我使用的是Centos 6. 我的Tomcat设置如下(Tomcat未部署任何应用程序):

CATALINA_OPTS="${CATALINA_OPTS}  -XX:+DisableAttachMechanism -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.local.only=true -Djava.rmi.server.useLocalHostname=true -Djava.rmi.server.useCodebaseOnly=true -Dcom.sun.management.jmxremote.port=9123 -Dcom.sun.management.jmxremote.rmi.port=9123"

Tomcat进程看起来像这样:

/usr/java/jdk1.7.0_51/bin/java -Djava.util.logging.config.file=/usr/tomcat-7.0.47/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -XX:+DisableAttachMechanism -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.local.only=true -Djava.rmi.server.useLocalHostname=true -Djava.rmi.server.useCodebaseOnly=true -Dcom.sun.management.jmxremote.port=9123 -Dcom.sun.management.jmxremote.rmi.port=9123 -Djava.endorsed.dirs=/usr/tomcat-7.0.47/endorsed -classpath /usr/tomcat-7.0.47/bin/bootstrap.jar:/usr/tomcat-7.0.47/bin/tomcat-juli.jar -Dcatalina.base=/usr/tomcat-7.0.47 -Dcatalina.home=/usr/tomcat-7.0.47 -Djava.io.tmpdir=/usr/tomcat-7.0.47/temp org.apache.catalina.startup.Bootstrap start

很遗憾,每次我看到额外的监听端口:

tcp        0      0 :::38830                    :::*                        LISTEN      790/java
tcp        0      0 ::ffff:127.0.0.1:8080       :::*                        LISTEN      790/java
tcp        0      0 :::9123                     :::*                        LISTEN      790/java

额外的运行:

tcp        0      0 ::ffff:127.0.0.1:8080       :::*                        LISTEN      2348/java
tcp        0      0 :::36252                    :::*                        LISTEN      2348/java
tcp        0      0 :::9123                     :::*                        LISTEN      2348/java

顺便问一下,为什么我在RMI端口之前看不到::ffff:127.0.0.1

第二次添加以澄清评论

这与Tomcat无关。 我已尝试使用类似的设置运行ant: Ant进程如下所示:

/usr/bin/java -XX:+DisableAttachMechanism -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.local.only=true -Djava.rmi.server.useLocalHostname=true -Djava.rmi.server.useCodebaseOnly=true -Dcom.sun.management.jmxremote.port=9123 -Dcom.sun.management.jmxremote.rmi.port=9123 -classpath /usr/apache-ant-1.9.2/lib/ant-launcher.jar -Dant.home=/usr/apache-ant-1.9.2 -Dant.library.dir=/usr/apache-ant-1.9.2/lib org.apache.tools.ant.launch.Launcher -cp  sleep

不幸的是,每次我看到额外的监听端口:

tcp        0      0 :::41200                    :::*                        LISTEN      13597/java
tcp        0      0 :::9123                     :::*                        LISTEN      13597/java

额外的运行:

tcp        0      0 :::58356                    :::*                        LISTEN      13629/java
tcp        0      0 :::9123                     :::*                        LISTEN      13629/java

答案:这是Java的bug。

我成功地在Java上发现了问题: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8035404


1
没有人解释为什么打开了3D端口(却没有被JConsole使用)。 - Michael
1
尝试按照答案中的链接进行操作 - 这个 链接特别有用。简短的回答是“这就是JMX的工作方式”。 - Boris the Spider
1
当你知道了会发生什么变化呢?它仍然会打开三个端口,因为“这就是JMX的工作方式”。在我看来,这就像问为什么Excel会锁定你在其中打开的文件;它只是以这种方式工作。知道确切的原因(你必须询问原始设计者)不会改变它的工作方式。 - Gimby
1
如果他们错误地打开了端口,我会在Java上报告一个bug :) 但这不是Excel的问题。 - Michael
我成功地在Java上打开了Bug:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8035404 - Michael
显示剩余6条评论
4个回答

135

与常见的看法相反,JMX/RMI并不需要打开所有这些端口。实际上,您可以强制它们相同,这意味着在一天结束时,您只需要在防火墙中打开一个洞(如果防火墙是您所担心的)。

尝试设置系统属性:

com.sun.management.jmxremote.port
com.sun.management.jmxremote.rmi.port

将它们明确地设置为相同的值可以防止RMI选择随机端口。将它们设置为相同的值可以确保它打开更少的监听端口。

这适用于Java 7更新25或更高版本。

第三个端口是什么?

您的应用程序打开的第三个端口(如果您遵循我的建议,则为第二个)由Java Attach API使用。JConsole用它来连接“本地进程”。自Java 6以来,默认启用Java Attach API功能,无论com.sun.management.jmxremote属性如何设置,该功能都会使用随机端口(也称为OS短暂端口),但实际上并不重要,因为该功能仅允许从主机本身进行连接。如果您真的不喜欢这个功能,则可以在命令行中添加-XX:+DisableAttachMechanism来禁用Java Attach API功能。然后,您将不再看到java进程(在本例中为Tomcat)侦听随机端口。

如何使JMX只侦听环回接口

对于自定义应用程序,您将使用RMIServerSocketFactory,但这是Tomcat,因此您必须使用Tomcat的JMX远程生命周期监听器

另一方面,由于Java 7现在具有com.sun.management.jmxremote.local.only属性,因此现在无关紧要了。它确保仅允许来自主机本身的连接。请注意,JMX库并未通过绑定到环回接口来实现这一点,后者可能是一种方法,但也略有不准确,因为主机可以潜在地具有多个环回接口。

实际上,从大体上讲(就JMX的最新JDK添加而言),我会说Tomcat的现在已经是多余的,除非您想绑定到某个非常奇怪的网络接口。


1
@Michael:为什么你只希望它绑定到环回接口?(我猜你有这个期望是因为 com.sun.management.jmxremote.local.only 属性吗?)请阅读我的答案,特别是标题为“如何使JMX仅侦听环回接口”的部分。 - peterh
4
我已经在Java上提交了Bug:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8035404。 - Michael
2
@DanielSerodio。我阅读了JDK源代码。你可以在sun.management.jmxremote.ConnectorBootstrap.java中找到它。 - peterh
4
谢谢@peterh。我尝试将两个端口都设置为相同的端口,但出现了“java.rmi.server.ExportException: Port already in use: 1099”的错误。在http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8035404上的评论中提到,这两个端口需要是独立的。 - Daniel Serodio
2
我仍然可以看到第三个端口已经打开并在所有接口上监听,这非常令人烦恼。我检查了错误的URL,但没有找到有用的信息,因为它说“不是问题”。此外,-XX:+ DisableAttachMechanism 没有给我任何东西。在这里要明确的是,我正在处理Kafka,并且真的希望控制所有其端口。有关如何禁用或绑定 Java Attach API 到特定接口的建议。谷歌没有给我任何有用的东西。 - Max
显示剩余13条评论

9
使用Oracle Java SE 1.8.0_121。 可以将jmxremote.port和jmxremote.rmi.port设置为相同的值,这样就少了一个打开的端口。还可以将jmxremote.host设置为127.0.0.1,使该端口(或这两个端口,如果您将它们设置为不同的值)仅绑定到回环接口。 但是,仍会动态分配另一个端口,并绑定到0.0.0.0。我无法使用-XX+DisableAttachMechanism来阻止此端口,也无法使其绑定到除0.0.0.0以外的其他内容。

虽然这不是一个答案,但我可以证实所描述的行为。 - simlev
@user2679436,你解决了第三个端口的问题吗? - mvsagar
@mvsagar 我能够使用上述方法将固定端口减少到一个。但是对于0.0.0.0上的动态端口,我无能为力,只能接受它的存在。 - user2679436
@user2679436,感谢您的更新。我也看到这是不可避免的。但是一个已提交的错误报告表明,在JDK 15中提供了一种选项来固定端口。我还没有尝试过。https://bugs.openjdk.java.net/browse/JDK-8237631 - mvsagar

3

“防火墙和NAT不友好”并没有解释实际的端口。 - user207421

1
第三个端口与RMI(JRMP)的工作方式无关,这是在错误地记录中 在bug中提到的。
也与Java Attach API无关。
它是本地JMX侦听器使用的端口。每当远程JMX启动时,由于某种原因本地JMX也会启动。可以在此来自OpenJDK的代码中看到。
        /*
         * If the jmxremote.port property is set then we start the
         * RMIConnectorServer for remote M&M.
         *
         * If the jmxremote or jmxremote.port properties are set then
         * we start a RMIConnectorServer for local M&M. The address
         * of this "local" server is exported as a counter to the jstat
         * instrumentation buffer.
         */
        if (jmxremote != null || jmxremotePort != null) {
            if (jmxremotePort != null) {
                jmxServer = ConnectorBootstrap.
                        startRemoteConnectorServer(jmxremotePort, props);
                startDiscoveryService(props);
            }
            startLocalManagementAgent();
        }

在文档中也提到了

为了能够从远程系统进行监控和管理,您必须在启动Java VM时设置以下系统属性:com.sun.management.jmxremote.port=portNum

在上面的属性中,portNum是您想要通过其启用JMX RMI连接的端口号。请确保指定未使用的端口号。除了发布本地访问的RMI连接器外,设置此属性还会在指定端口上使用“jmxrmi”这个众所周知的名称,在私有只读注册表中发布另一个RMI连接器。

此外,自2020年以来,本地端口可以通过com.sun.management.jmxremote.local.port进行配置


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接