Jconsole 開啟遠端連線遇到的一些坑

2023-06-20 15:00:47

最近在學習 JVM,其中涉及到效能、記憶體等指標分析需要使用工具分享,Java 提供了幾個視覺化工具來監控和管理 Java 應用,比如 Jconsole、JVisual、JMC,他們以圖形化的介面實時的監控程式各種效能指標以及記憶體、CPU 的使用情況。

Jconsole、JVisual、JMC 視覺化工具,呼叫本地監控直接使用對應的命令列即可,但 Linux 無法使用視覺化工具,Java 程式基本都部署到 Linux 伺服器。需要本地遠端呼叫伺服器,本文記錄一下遠端呼叫的一些步驟和遇到的坑。

JMX

JXM(Java Management Extensions) 是 Java 提供的一套標準 API,用於管理和監控 Java 應用程式的各種效能指標和使用情況。這裡主要使用遠端存取的功能。

JMX 啟動引數:

  • -Dcom.sun.management.jmxremote 遠端開啟開關
  • -Dcom.sun.management.jmxremote.port=1808 jmx遠端呼叫埠
  • -Dcom.sun.management.jmxremote.authenticate=false 不開啟驗證
  • -Dcom.sun.management.jmxremote.ssl=false 不為ssl連線
  • -Djava.rmi.server.hostname=34.126.141.21 伺服器所在ip或者域名

設定遠端連線

啟動 Java 程式一般有兩種方式:

  • 一是打成 jar 包,使用 java -jar 執行程式。
  • 一種是打成 war 包,放在 tomcat 上執行。

無論是 jar 還是 war 包,都是將上面的設定引數用空格拼接起來,比如將上面的引數拼接:

Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=18088 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=34.126.141.21

新增到組態檔或者啟動引數中。

Java 程式啟動

jar 包程式啟動一般為:

java -jar xxx.jar

新增引數後:

java -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1808 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=34.126.141.21 -jar xxx.jar

tomcat 啟動

在啟動檔案 catalina.sh 裡面新增:

JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1808 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=34.126.141.21"

新增上面的設定之後,重啟 tomcat,再使用 Jconsole 遠端連線

使用 jconsole 遠端連線,一直連線不上

無法遠端問題排查

先檢視本地埠是否開啟:

netstat -ntlp

埠已開啟:

tcp6     0     0 :::1808          :::*              LISTEN      9087/java

再檢視是否是防火牆問題,使用埠掃描檢視,埠也開啟了:

埠開啟了,但是還是無法連線

找了很多網上的答案,大家都是抄來抄去的,都是上面的設定。最後才發現少了 rmi 設定。

解決方案

新增 rmi 埠:

-Dcom.sun.management.jmxremote.rmi.port=1808

JMX 和 RMI,是兩種相關聯的技術,JMX 使用 RMI 作為遠端管理工具來管理和監控 Java 程式,RMI 為 JMX 提供了遠端連線所需的遠端呼叫和通訊機制。

新增了上面的設定,就能遠端監控 Java 服務了。

關閉 tomcat 報錯

tomcat 啟動新增了設定之後,關閉 tomcat 服務時,就報錯了:

sun.management.AgentConfigurationError: java.rmi.server.ExportException: Port already in use: 18088; nested exception is: 
	java.net.BindException: Address already in use (Bind failed)
	at sun.management.jmxremote.ConnectorBootstrap.exportMBeanServer(ConnectorBootstrap.java:800)
	at sun.management.jmxremote.ConnectorBootstrap.startRemoteConnectorServer(ConnectorBootstrap.java:468)
	at sun.management.Agent.startAgent(Agent.java:262)
	at sun.management.Agent.startAgent(Agent.java:452)
Caused by: java.rmi.server.ExportException: Port already in use: 18088; nested exception is: 
	java.net.BindException: Address already in use (Bind failed)
	at sun.rmi.transport.tcp.TCPTransport.listen(TCPTransport.java:346)
	at sun.rmi.transport.tcp.TCPTransport.exportObject(TCPTransport.java:254)
	at sun.rmi.transport.tcp.TCPEndpoint.exportObject(TCPEndpoint.java:412)
	at sun.rmi.transport.LiveRef.exportObject(LiveRef.java:147)
	at sun.rmi.server.UnicastServerRef.exportObject(UnicastServerRef.java:237)
	at sun.management.jmxremote.ConnectorBootstrap$PermanentExporter.exportObject(ConnectorBootstrap.java:199)
	at javax.management.remote.rmi.RMIJRMPServerImpl.export(RMIJRMPServerImpl.java:146)
	at javax.management.remote.rmi.RMIJRMPServerImpl.export(RMIJRMPServerImpl.java:122)
	at javax.management.remote.rmi.RMIConnectorServer.start(RMIConnectorServer.java:404)
	at sun.management.jmxremote.ConnectorBootstrap.exportMBeanServer(ConnectorBootstrap.java:796)

簡單就是埠被佔用了,又去網上搜了很多答案,彙總了兩個解決方法:

  • 把 catalina.sh 新增的設定刪掉
  • 使用 kill -9 的命令直接殺掉程序

這兩種方案都是治標不治本的方法,每次都要做多一點的操作,就顯得很繁瑣。

問題分析

無論使用 startup.sh 啟動 tomcat 還是使用 shutdown.sh 關閉 tomcat 都會執行 catalina.sh 指令碼,所以關閉 tomcat 也會啟動埠,而啟動 tomcat 的時候已經開啟了埠,關閉的時候再開啟就報錯了。

問題解決

只在啟動 tomcat 時新增 jmx 相關的設定,在 catalina.sh 新增判斷條件 if [ "$1" = "start" ]

if [ "$1" = "start" ] ; then
  JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=18088 -Dcom.sun.management.jmxremote.rmi.port=18088 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=34.126.141.211"
fi

總結

  • 網上都是相互抄來抄去的,都是缺少 RMI 設定,完整設定如下
    • JAVA_OPTS="$JAVA_OPTS -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=1808 -Dcom.sun.management.jmxremote.rmi.port=1808 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=34.126.141.21"
  • JMX 是一套標準 API,用於管理和監控 Java 應用程式。而 RMI 為 JMX 提供了遠端連線。
  • 關閉的報錯的解決方案也是互相抄來抄去,解決方案都是治標不治本。在設定上面新增 if 判斷條件 新增判斷條件 if [ "$1" = "start" ]。