最近同事在迁移一个老的应用到容器中去,这个应用里面使用了jmx服务,配置的调用地址如:service:jmx:rmi://172.20.32.36/jndi/rmi://172.20.32.36:1072/remoteRMI
,迁移完成后使用 jmc
工具去测试连接,会发现连接失败,而这个现象很诡异,有的机器发布的容器可以连接成功。询问了开发人员,回复是这个 jmx 服务认机器,以前不在容器中部署也存在这个问题。这其实是一个没有答案的回复了,但没办法,我们必须要将这个应用部署到容器中,只能自己好好分析分析了,我相信在不同的机器使用镜像启动应该都是一致的,既然有机器可以使用,那么肯定是机器配置哪里有不一样。
排查步骤
- 防火墙的问题:主监听端口设置了允许访问,但是随机端口号是 Java 进程启动后,OS 随机分配给 jmxserver 的,如果不关闭防火墙,就必须在每次 server 就绪后,检测一下随机端口,然后设置为允许访问。(我检查服务器防火墙都是关闭状态的,netstat 与 telnet 也都是正常的,可以排除被墙的问题)
netstat -ntlp |grep 1072
tcp6 0 0 :::1072 :::* LISTEN 36496/java
telnet 172.20.32.36 1072
- tomcat 启动 cataina.sh 时参数 CATALINA_OPTS 配置问题。(尝试增加配置后问题依旧)
-Dcom.sun.management.jmxremote=true \
-Dcom.sun.management.jmxremote.port="1072" \
-Dcom.sun.management.jmxremote.ssl=false \
-Dcom.sun.management.jmxremote.authenticate=false \
-Djava.rmi.server.hostname=172.20.32.36 \
- hostname 的问题:我比较了可用和不可用的 hostname 、/etc/hosts、/etc/resolv.conf 等文件,没有发现什么差异,这里有被误导,很多资料仅仅说 hostname 有问题,并没有说要怎么去查看哪里有问题,最开始用 hostname 检查肯定看不出问题,直到兜了一大圈回来,在一个测试论坛找到了答案,应该用 hostname -i 来检查,于是发现了差异。
[root@es-36 /]# hostname -i
127.0.0.1
[root@es-127 /]# hostname -i
172.20.32.127
那么问题来了,这个 hostname -i 又是从哪里取值的呢,其实很简单,就是在 /etc/hosts 配置的,如下:
[root@es-36 ~]# cat /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
# 127.0.0.1 es-36
172.20.32.36 es-36
[root@es-36 ~]# hostname -i
172.20.32.36
将 127.0.0.1 修改为机器的物理 IP 发现 hostname -i 正常,jmx 服务也能正常访问了。
参考资料
JDK-6209663 : jconsole won’t connect to remote JVM on Linux
Monitoring and Management Using JMX
JDK-8035404 : Java opens random 3-d port when JMX is configured
Why Java opens 3 ports when JMX is configured?
Monitoring and Management Using JMX Technology
通过【jmx rmi 穿越防火墙问题及jmxmp的替代方案】解决了随机端口产生的问题,原因就是:
JMXConnectorServer的JMXServiceURL为如下形式:service:jmx:rmi://localhost:5000/jndi/rmi://localhost:6000/jmxrmi 则首先客户端连接到rmiregistry上得到真实服务器的stub(如rmi://localhost:6000/rmxrmi),然后客户端再根据该stub连接到真实的服务器上(如rmi://localhost:5000)。如果jmx服务端省略了蓝色部分的标注,默认的通信端口是随机产生的。
解决办法:就是代码里面的连接要使用2个端口即可。哎,找了好久。经实测,这2个端口可以用同一个,在 docker 中使用实践通过哦,^ _ ^ 。
总结
从这找错的过程来看,其实并不算太复杂,因为我算是 java 基础比较弱的,这个 jmx 也是第一次接触,但为什么我能找到专业开发人员都无法找出的问题呢?可能我比他们多一些耐心和钻研精神吧。我想对技术应该要有这种态度,多思考问题,才能将技术学习得更扎实。