伺服器開啟JMX監控
阿新 • • 發佈:2018-12-25
JMX是一個框架,提供了一種功能,可以實時查詢應用程式中通過JMX向外部公佈的相應引數或者是其他應用程式,同時也可以通過JMX來實時地呼叫應用程式使用JMX向外部公佈的介面,來完成一些功能操作。
如果沒有修改之前的兩個檔案,那麼直接使用“monitorRole/QED”(或controllerRole)登入即可。 如果需要定製該服務,修改access和password檔案即可,可以定製其許可權。 注意,如果發現通過JVisualVM無法看到CPU,MXBean的一些狀態資訊,考慮可能是由於JVM啟動引數host錯誤。
通過JMX可以輕鬆地為應用程式新增管理功能,在儘可能少的改變原有系統程式碼基礎上實現對原系統的管理。 註冊時使用MBeanServer進行註冊的操作,給定對應的mbean,mbeanServer會根據該bean實現的介面去尋找其對應的MBean定義。
如果想要對遠端伺服器的程序進行監控,需要在伺服器進行相關設定,啟動守護程序。如果想進一步定製自己的MXBean,可以考慮在應用程式中registerMXBean,當然在web應用中也可以藉助Spring MBean相關類庫進行該操作。
開啟JStatd守護程序
編寫配置檔案 jstatd.all.policy策略檔案:grant codebase "file:${java.home}/../lib/tools.jar" {
permission java.security.AllPermission;
};
開啟jstatd守護程序,並開啟日誌(注意線上開啟日誌可能會導致磁碟空間佔用較大)
如果 開啟日誌功能,則會導致生成的日誌檔案比較大,通常幾周時間可能會超過2G,為了磁碟考慮,在執行良好的伺服器上執行-J-Djava.rmi.server.logCalls=false即可。nohup jstatd -J-Djava.security.policy=/home/java/jstatd.all.policy -J-Djava.rmi.server.logCalls=true &
在啟動tomcat的時候(修改執行tomcat的檔案,catalina.sh),需要加入以下JVM引數:jstatd -J-Djava.security.policy=jstatd.all.policy Could not create remote object access denied ("java.util.PropertyPermission" "java.rmi.server.ignoreSubClasses" "write") java.security.AccessControlException: access denied ("java.util.PropertyPermission" "java.rmi.server.ignoreSubClasses" "write") at java.security.AccessControlContext.checkPermission(AccessControlContext.java:457) at java.security.AccessController.checkPermission(AccessController.java:884) at java.lang.SecurityManager.checkPermission(SecurityManager.java:549) at java.lang.System.setProperty(System.java:789) at sun.tools.jstatd.Jstatd.main(Jstatd.java:139)
-Dcom.sun.management.jmxremote.port=8888 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=192.168.1.165
說明:
- -Dcom.sun.management.jmxremote.port:這個是配置遠端connection的埠號的,要確定這個埠沒有被佔用;
- -Dcom.sun.management.jmxremote.ssl=false 指定了 JMX 是否啟用 ssl;
- -Dcom.sun.management.jmxremote.authenticate=false指定了JMX 是否啟用鑑權(需要使用者名稱,密碼鑑權),
- 2,3兩個是固定配置,是 JMX的遠端服務許可權的;
- -Djava.rmi.server.hostname:這個是配置server的IP的;
Could not bind /JStatRemoteHost to RMI Registry
java.rmi.ConnectIOException: Exception creating connection to: 0.0.0.2; nested exception is:
java.net.SocketException: Invalid argument or cannot assign requested address
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:631)
at sun.rmi.transport.tcp.TCPChannel.createConnection(TCPChannel.java:216)
at sun.rmi.transport.tcp.TCPChannel.newConnection(TCPChannel.java:202)
at sun.rmi.server.UnicastRef.newCall(UnicastRef.java:341)
at sun.rmi.registry.RegistryImpl_Stub.rebind(Unknown Source)
at java.rmi.Naming.rebind(Naming.java:177)
at sun.tools.jstatd.Jstatd.bind(Jstatd.java:57)
at sun.tools.jstatd.Jstatd.main(Jstatd.java:143)
Caused by: java.net.SocketException: Invalid argument or cannot assign requested address
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:339)
at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:200)
at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:182)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
at java.net.Socket.connect(Socket.java:579)
at java.net.Socket.connect(Socket.java:528)
at java.net.Socket.<init>(Socket.java:425)
at java.net.Socket.<init>(Socket.java:208)
at sun.rmi.transport.proxy.RMIDirectSocketFactory.createSocket(RMIDirectSocketFactory.java:40)
at sun.rmi.transport.proxy.RMIMasterSocketFactory.createSocket(RMIMasterSocketFactory.java:147)
at sun.rmi.transport.tcp.TCPEndpoint.newSocket(TCPEndpoint.java:613)
... 7 more
通過閱讀下面的文件 http://my.oschina.net/xiaotian120/blog/207015,經過伺服器上的一番調研,發現問題出現在hostname上,服務端呼叫 hostname -i 命令,顯示出來的伺服器主機名為0.0.0.2(這一點日誌中有體現)。
JMX監控認證
如果我們將jmxremote的認證設定為false,此時在進行JMX關聯時不會進行認證,如果需要認證,將其設定為true:-Dcom.sun.management.jmxremote.authenticate=true
但此時同樣需要增加引數,用來指定jmx遠端監控的訪問檔案以及密碼檔案。
-Dcom.sun.management.jmxremote.access.file=/usr/java/jdk1.6.0_32/jre/lib/management/jmxremote.access -Dcom.sun.management.jmxremote.password.file=/usr/java/jdk1.6.0_32/jre/lib/management/jmxremote.password"
啟動java程序時出現下面的錯誤:
錯誤: 必須限制口令檔案讀取訪問許可權: /home/java/jmxremote.password
這是由於兩個檔案jmxremote.access, jmxremote.password的訪問許可權有問題,必須要按照如下規則設定:
chmod 400 jmxremote.access 要求該檔案是任何使用者均不可寫的
chmod 700 jmxremote.password 一定注意這個檔案預設是不可寫的
此時,如果在外部需要連線該jmx連線時,請求提供安全憑證:
如果沒有修改之前的兩個檔案,那麼直接使用“monitorRole/QED”(或controllerRole)登入即可。 如果需要定製該服務,修改access和password檔案即可,可以定製其許可權。 注意,如果發現通過JVisualVM無法看到CPU,MXBean的一些狀態資訊,考慮可能是由於JVM啟動引數host錯誤。
新建第一個MXBean
通過JMX可以輕鬆地為應用程式新增管理功能,在儘可能少的改變原有系統程式碼基礎上實現對原系統的管理。 註冊時使用MBeanServer進行註冊的操作,給定對應的mbean,mbeanServer會根據該bean實現的介面去尋找其對應的MBean定義。
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
ControllerMBean controllerMBean = new ControllerBean();
mbs.registerMBean(controllerMBean, new ObjectName("MyAppmbean:name=controller"));
在第一次執行時出現如下問題:
javax.management.NotCompliantMBeanException: jmx.ControllerBean: Class jmx.ControllerBean is not a JMX compliant MXBean
at com.sun.jmx.mbeanserver.Introspector.throwException(Introspector.java:466)
at com.sun.jmx.mbeanserver.Introspector.getMXBeanInterface(Introspector.java:357)
at com.sun.jmx.mbeanserver.Introspector.checkCompliance(Introspector.java:166)
at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.registerMBean(DefaultMBeanServerInterceptor.java:317)
at com.sun.jmx.mbeanserver.JmxMBeanServer.registerMBean(JmxMBeanServer.java:522)
at jmx.Main.main(Main.java:16)
經過分析JMX.isMXBeanInterface方法,可以看出,在registerMBean的時候,會根據當前類實現的介面列表,查詢符合MXBean定義的介面,如果介面不符合MXBean的定義就會丟擲上面的錯誤,如何使得介面滿足MXBean型別,一種是介面名稱以“MXBean”結尾,一種是加入@MXBean Annotation。
public static boolean isMXBeanInterface(Class<?> interfaceClass) {
if (!interfaceClass.isInterface())
return false;
if (!Modifier.isPublic(interfaceClass.getModifiers()) &&
!Introspector.ALLOW_NONPUBLIC_MBEAN) {
return false;
}
MXBean a = interfaceClass.getAnnotation(MXBean.class);
if (a != null)
return a.value();
return interfaceClass.getName().endsWith("MXBean");
// We don't bother excluding the case where the name is
// exactly the string "MXBean" since that would mean there
// was no package name, which is pretty unlikely in practice.
}
Spring與JMX的整合
對於標準的MBean來說,主要是通過MBeanExporter來實現的,比如上面我們提到的MBean,就可以通過配置mbServer以及mbean的方式管理MXBean:
<bean id="mbServer" class="org.springframework.jmx.export.MBeanExporter">
<property name="autodetectModeName" value="AUTODETECT_ALL"/>
</bean>
<bean name="mydomain:myobj=MyObjectMBean" class="com.api.example.jmx.ControllerBean"/>
上面的配置中,MBeanExporter會查詢本地MBean Server,指定的探測模式autodetectModeName為AUTODETECT_ALL,無需手動向MBean Server進行註冊,便可以管理配置中的MBean物件。
基於註解的MBean管理
@ManagedResource表示指定該類的例項作為MBean註冊到MBean Server中,然後通過對屬性和方法分別使用@ManagedAttribute和@ManagedOperation來指定暴露的屬性和方法,@Component
@ManagedResource(objectName = "org.springexample.jmx:name=ServerManager", description = "Server Manager")
public class ServerManagerImpl {
private String serverName = "springServer";
private boolean serverRunning = true;
private int minPoolSize = 5;
private int maxPoolSize = 10;
@ManagedAttribute(description = "The server name.")
public String getServerName() {
return serverName;
}
@ManagedAttribute(description = "Server's running status.")
public boolean isServerRunning() {
return serverRunning;
}
@ManagedAttribute(description = "Whether or not the server is running.", currencyTimeLimit = 20, persistPolicy = "OnUpdate")
public void setServerRunning(boolean serverRunning) {
this.serverRunning = serverRunning;
}
@ManagedOperation(description = "Change db connection pool size.")
@ManagedOperationParameters({
@ManagedOperationParameter(name = "min", description = "Minimum pool size."),
@ManagedOperationParameter(name = "max", description = "Maximum pool size.")
})
public int changeConnectionPoolSize(int minPoolSize, int maxPoolSize) {
Assert.isTrue(minPoolSize > 0, "Minimum connection pool size must be larger than 0, min=" + minPoolSize);
Assert.isTrue(maxPoolSize > minPoolSize, String.format("Minimum connection pool size must be smaller than maximum, min=%s, max=%s", minPoolSize, maxPoolSize));
this.minPoolSize = minPoolSize;
this.maxPoolSize = maxPoolSize;
int diff = maxPoolSize - minPoolSize;
Random random = new Random();
int currentSize = (minPoolSize + random.nextInt(diff));
return currentSize;
}
}
基於spring基於註解的MBean管理可以針對一個普通的Java類,指定暴露的屬性和方法,此時在spring applicationContext中宣告的內容就要少很多,只需要宣告掃描的package,以及mbean-export即可。
<context:component-scan base-package="com.api.example"/>
<context:mbean-export/>
這裡我們就可以通過JVisualVM來簡單地動態修改類似池化的一些配置,並使其生效
。
<!--?xml version="1.0" encoding="UTF-8"?-->
注意事項
如果能夠通過jmx連線,但有些資訊無法顯示,比如cpu,visual gc等,請檢查一下hostname或java程序啟動的jmx主機名稱是否與hostname一致;
程序無法找到,請確保啟動的jstatd和java程序是同一個使用者(以前就犯過這種錯誤,java程序使用root使用者啟動,而jstatd是普通使用者,於是看不到該java程序)。