JMX是一個框架,提供了一種功能,能夠實時查詢應用程序中經過JMX向外部公佈的相應參數或者是其餘應用程序,同時也能夠經過JMX來實時地調用應用程序使用JMX向外部公佈的接口,來完成一些功能操做。
若是想要對遠程服務器的進程進行監控,須要在服務器進行相關設置,啓動守護進程。若是想進一步定製本身的MXBean,能夠考慮在應用程序中registerMXBean,固然在web應用中也能夠藉助Spring MBean相關類庫進行該操做。html
開啓JStatd守護進程
編寫配置文件 jstatd.all.policy策略文件:
grant codebase "file:${java.home}/../lib/tools.jar" { permission java.security.AllPermission; };
開啓jstatd守護進程,並開啓日誌(注意線上開啓日誌可能會致使磁盤空間佔用較大)
nohup jstatd -J-Djava.security.policy=/home/java/jstatd.all.policy -J-Djava.rmi.server.logCalls=true &
若是 開啓日誌功能,則會致使生成的日誌文件比較大,一般幾周時間可能會超過2G,爲了磁盤考慮,在運行良好的服務器上執行-J-Djava.rmi.server.logCalls=false便可。
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)
http://docs.oracle.com/javase/7/docs/technotes/tools/share/jstatd.html,編寫該文件不須要自做聰明地將java.home設置成其餘屬性,只須要按照文檔中說明的便可。
在啓動tomcat的時候(修改執行tomcat的文件,catalina.sh),須要加入如下JVM參數:
-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的;
在CentOS環境中啓動jstatd仍然出現錯誤,
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(這一點日誌中有體現)。 java
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鏈接時,請求提供安全憑證:
![](http://static.javashuo.com/static/loading.gif)
若是沒有修改以前的兩個文件,那麼直接使用「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來簡單地動態修改相似池化的一些配置,並使其生效
。