JConsole通过SSH本地端口转发

71
我希望能够远程连接到一个暴露了JMX的Java服务,但是由于防火墙的限制,我无法连接。我尝试使用ssh本地端口转发,但是连接失败。从wireshark上看,当你尝试使用jconsole连接时,它想要在连接到9999端口后通过一些临时端口进行连接,而这些端口被防火墙阻止了。
有没有办法让jconsole只通过9999端口连接或使用代理?这篇文章是否仍然是最佳解决方案?还是说我漏掉了什么?

请查看我对类似问题的回答:https://dev59.com/iGcs5IYBdhLWcg3wdz1m#15086467 - blank
4个回答

127

使用SSH socks隧道可以更加简便地完成此操作,因为JConsole支持SOCKS:

  1. 在某个可用端口(例如7777)上本地创建SSH socks代理:

    ssh -fN -D 7777 user@firewalled-host

  2. 通过指定SOCKS代理(例如localhost:7777)和JMX服务器的地址(例如localhost:2147),运行JConsole:

    jconsole -J-DsocksProxyHost=localhost -J-DsocksProxyPort=7777 service:jmx:rmi:///jndi/rmi://localhost:2147/jmxrmi -J-DsocksNonProxyHosts=

正如下面的一个答案中所提到的那样,在JDK 8u60+中还需要加上选项-J-DsocksNonProxyHosts=才能使其正常工作。


如果您在远程主机上运行了一个仅通过ssh可用的jvm,如果您可以重新启动该jvm并修改其远程管理选项,并且您只想将本地jconsole连接到此远程jvm,则可以采用这种方法。对我很有效。非常感谢。 - yohann.martineau
1
对于第一部分,如果您不需要远程SSH会话(仅需要端口转发),请添加“-N”标志,例如:ssh -N -D 7777 user@host - Wes Johnson
谢谢 - 我尝试了许多不同的方法来实现远程JMX连接,而这是唯一对我有效的方法。 - Junho Park
我正在连接到一个AWS服务器,这是唯一有效的解决方案。 - chubbsondubs
1
请问您能告诉我在您的示例中"localhost:2147"来自哪里吗? - JJ Roman
显示剩余5条评论

70
几乎所有当前的JDK版本(7u25或更高版本)现在都可以很容易地通过SSH使用JConsole和Visual JVM了(因为现在可以将JMX绑定到单个端口)。
我使用以下JVM参数。
-Dcom.sun.management.jmxremote.port=8090
-Dcom.sun.management.jmxremote.rmi.port=8090
-Djava.rmi.server.hostname=127.0.0.1
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false

然后我启动SSH连接

ssh my.javaserver.domain -L 8090:127.0.0.1:8090

当我可以从JConsole连接时

远程进程: -> localhost:8090

以及Java Visual VM

右键单击本地 -> 添加JMX连接 -> localhost:8090


1
尝试了这么多方法后,你的答案真的起作用了!非常感谢。 - teymourlouie
1
非常感谢!我尝试了所有其他方法,而这是唯一有效的一个。 - John A
4
太棒了,正是我寻找的东西。以下是我在使用VisualVM时的个人观察:java.rmi.server.hostname=localhost(而不是IP地址)也可以正常工作... 另外,属性com.sun.management.jmxremote.portcom.sun.management.jmxremote.rmi.port必须具有相同的值。 - Abdull
1
哇哦!!!6个小时后。天啊,感觉你拯救了世界,亲爱的朋友!!! - Aadam
没错,这起作用了。在Ubuntu 16.04上,通过Vagrant VirtualBox,使用Tomcat7和Java8。谢谢! - ghukill
显示剩余2条评论

45
有没有办法让jconsole只通过9999连接或使用代理?这篇文章仍然是最好的解决方案吗?还是我漏掉了什么?
是的,那篇文章大致正确。
当您在服务器上指定JMX端口(-Dcom.sun.management.jmxremote.port=####)时,您实际上只指定了应用程序的注册表端口。当您连接后,它提供了一个额外的服务器端口,jconsole实际上使用该端口进行所有工作。要使转发起作用,您需要知道注册表和服务器端口。
以下类似的内容应该可以将您的应用程序运行时的注册表和服务器端口都设置为8000。请参见此处以获取更多详细信息
-Dcom.sun.management.jmxremote.port=8000
-Dcom.sun.management.jmxremote.rmi.port=8000
-Djava.rmi.server.hostname=127.0.0.1

作为旁注,我的SimpleJMX库允许您轻松设置两个端口,并且您可以将它们都设置为相同的端口。
因此,一旦您知道需要转发的两个端口,就可以设置您的ssh命令。例如,如果您将注册表和服务器端口配置为8000,则应执行以下操作:
ssh -L 8000:localhost:8000 remote-host

这将创建一个本地端口8000,它将转发到远程主机上的localhost:8000。如果您需要转发多个端口,则可以指定多个-L参数。然后,您可以将jconsole连接到localhost:8000,它将适当地连接到远程主机。
另外,如果您的服务器有多个接口,您可能需要设置java.rmi.server.hostname变量以绑定到正确的接口。
-Djava.rmi.server.hostname=10.1.2.3

好的,谢谢。我本来希望这不是答案,因为我真的不想写额外的代码来监视可能有很多实例的服务。然而,你的答案非常清晰地描述了发生了什么,这很有帮助。 - blockcipher
如果你正在编写自己的JMX代码@blockcipher,你真的应该看一下我的SimpleJMX库。它真的让JMX编码变得非常容易。http://256.com/sources/simplejmx/ - Gray
我尝试了很多方法,只有在我打开门、直接连接并使用这个特定形式的URL“service:jmx:…”后才起作用。谢谢! - dividebyzero

13

在继续使用SSH代理方法时,针对较新的Java版本(大约8u66),您还需要将socksNonProxyHosts设置为空,以避免出现以下情况:

jconsole -J-DsocksProxyHost=localhost -J-DsocksProxyPort=7777 -J-DsocksNonProxyHosts=

救星来了!Java 8u60也需要它。 - Jose Alban
2
“-J-DsocksNonProxyHosts=” 对我很有帮助! - javaPhobic

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