問題背景討論:在JAVA運行時,對JVM系統的檢測、管理是開發同學一直期望的,目前有一些開源產品開源針對JVM進行監控,例如javamelody等等,這樣框架雖然好,但是如果使用會帶來一些問題。第一:由於javamelody入口是一個filter,所以會帶來性能問題、第二:如果應用服務器很多,會缺乏統一管理,此外安全性、爬蟲等等問題略過不提
JDK自帶了故障檢測工具Jconsole和1.6出來的Jvisualvm都可以遠程對服務器進行監控,後者甚是提供可以在線得到ThreadDump和HeapDump文件,更是提供了可擴展插件功能(雖然可能會影響jvm服務器性能)
但現狀很多公司都有自己的運維團隊,未必會把線上服務器的權限交給我們開發同學,所以有必要了解JAVA遠程管理方面的知識,假設如果在後臺有一套系統,可以管理、監控我們線上所有java服務器,如果出現問題不需要對每個服務器進行排查而是直接從列表中獲取、定位到信息,豈不是一件很幸福的事情~
書歸正文,分享下近幾天在JMX方面的知識,JDK的API中翻譯過來對javax.management的描述
提供 Java Management Extensions 的核心類。
Java Management Extensions (JMXTM) API 是一個用於管理和監視的標準 API。典型用途包括:
1) 查詢並更改應用程序配置
2) 累積有關應用程序行爲的統計數據並使其可用
3) 通知狀態更改及錯誤狀況。
4) JMX API 還可以作爲解決方案的一部分來管理系統、網絡等。
5) API 包括遠程訪問,遠程管理程序可以基於這些目的與正在運行的應用程序進行交互。
JMX核心類爲Mbean。MBean 是表示資源的指定管理對象。它有一個管理接口,該接口包括以下內容:
1) 可以讀取和/或寫入的指定名稱和類型的屬性
2) 可以調用的指定名稱和類型的操作
3) 可以由 MBean 發送的指定類型的通知。
推薦一篇文章可以讓大家入門
http://www.ibm.com/developerworks/cn/java/j-lo-jse63/
配置遠程連接tomcat需要在tomcat所在服務器修改幾個配置
1) 修改JMX配置
進入JAVA_HOME/jre/lib/management路徑下面
將jmxremote.password.template改成jmxremote.password,將內容中的monitorRole QED的註釋去掉
將management.properties中的com.sun.management.jmxremote.port=(改成你想要的JMX端口號)
將 com.sun.management.jmxremote.ssl=false、 com.sun.management.jmxremote.authenticate=false註釋打開
注意:linux下需要該權限,chmod 600 jmxremote.access, chmod 600 jmxremote.password
window下特麻煩,現需要jdk裝在NTFS文件系統下,選中文件,點右鍵“屬性”-〉安全,點“高級”,去掉“從父項繼承....”,彈出窗口中選“刪除”,這樣刪除了所有訪問權限。再選“添加”-〉高級,“立即查找”,選中你的用戶,例administrator,點“確定",“確定"。來到權限窗口,勾選"完全控制",點"確定",OK了。
用jconsole連接遠程linux服務時, IP地址和port都輸入正確的情況下,仍然是連接失敗
vi /etc/hosts,將hostname對應的ip改爲真實ip
2) 修改tomcat啓動參數
windows下面爲catalina.bat、linux下面爲cataina.sh
set CATALINA_OPTS=-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port="JMXport"
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
如果不修改的時候默遠程連接不上,遠程連接的通過IP和JMX端口找到JVM虛擬機後無法定位具體哪個應用使用JMX端口,所以只有在啓動tomcat情況後佔用JMX
關於配置JMX,可以具體參考http://download.oracle.com/javase/1.5.0/docs/guide/management/agent.html#remote
實戰遠程連接TOMCAT
1) 獲取連接
privatestaticvoid getConnection() throws Exception {
//用戶名、密碼
Map<String, String[]> map = new HashMap<String, String[]>();
map
.put("jmx.remote.credentials", new String[] { "monitorRole",
"QED" });
String jmxURL = "service:jmx:rmi:///jndi/rmi://192.168.0.100:1000/jmxrmi";
JMXServiceURL serviceURL = new JMXServiceURL(jmxURL);
connector = JMXConnectorFactory.connect(serviceURL, map);
mbsc = connector.getMBeanServerConnection();
}
2) 獲取所有的objectName
Set MBeanset = mbsc.queryMBeans(null, null);
Iterator MBeansetIterator = MBeanset.iterator();
while (MBeansetIterator.hasNext()) {
ObjectInstance objectInstance = (ObjectInstance) MBeansetIterator
.next();
ObjectName objectName = objectInstance.getObjectName();
MBeanInfo objectInfo = mbsc.getMBeanInfo(objectName);
System.out.print("ObjectName:" + objectName.getCanonicalName()
+ ".");
System.out.print("mehtodName:");
for (int i = 0; i < objectInfo.getAttributes().length; i++) {
System.out.print(objectInfo.getAttributes()[i].getName() + ",");
}
System.out.println();
}
3) 獲取堆內存信息
ObjectName heapObjName = new ObjectName("java.lang:type=Memory");
//堆內存
MemoryUsage heapMemoryUsage = MemoryUsage
.from((CompositeDataSupport) mbsc.getAttribute(heapObjName,
"HeapMemoryUsage"));
long commitMemory = heapMemoryUsage.getCommitted();// 堆當前分配
long usedMemory = heapMemoryUsage.getUsed();
System.out.print("堆內存總量:"+heapMemoryUsage.getMax()/1024+"KB,當前分配量:"+commitMemory/1024+"KB,當前使用率:"+usedMemory/1024+"KB,");
System.out.println("堆內存使用率:" + (int) usedMemory * 100
/ commitMemory + "%");// 堆使用率
//棧內存
MemoryUsage nonheapMemoryUsage = MemoryUsage
.from((CompositeDataSupport) mbsc.getAttribute(heapObjName,
"NonHeapMemoryUsage"));
long noncommitMemory = nonheapMemoryUsage.getCommitted();
long nonusedMemory = heapMemoryUsage.getUsed();
System.out.println("棧內存使用率:" + (int) nonusedMemory * 100
/ noncommitMemory + "%");
//PermGen內存
ObjectName permObjName = new ObjectName(
"java.lang:type=MemoryPool,name=Perm Gen");
MemoryUsage permGenUsage = MemoryUsage
.from((CompositeDataSupport) mbsc.getAttribute(permObjName,
"Usage"));
long committed = permGenUsage.getCommitted();// 持久堆大小
long used = heapMemoryUsage.getUsed();//
System.out.println("perm gen:" + (int) used * 100 / committed
+ "%");// 持久堆使用率
4) 獲取堆中伊甸區
ObjectName youngHeapObjName = new ObjectName("java.lang:type=MemoryPool,name=Eden Space");
// 獲取Mbean對象
MBeanInfo youngHeapInfo = mbsc.getMBeanInfo(youngHeapObjName);
// 獲取對象的屬性
MBeanAttributeInfo[] youngHeapAttributes = youngHeapInfo.getAttributes();
MemoryUsage youngHeapUsage = MemoryUsage
.from((CompositeDataSupport) mbsc.getAttribute(youngHeapObjName, "Usage"));
System.out.print("目前新生區分 配最大內存:"+youngHeapUsage.getMax()/1024+"KB,");
System.out.print("新生區已分配:"+youngHeapUsage.getCommitted()/1024+"KB,");
System.out.print("新生區初始化:"+youngHeapUsage.getInit()/1024+"KB,");
System.out.println("新生區已使用"+youngHeapUsage.getUsed()/1024+"KB");
5) 獲取線程
ObjectName managerObjName = new ObjectName(
"Catalina:type=Manager,*");
Set<ObjectName> s = mbsc.queryNames(managerObjName, null);
for (ObjectName obj : s) {
ObjectName objname = new ObjectName(obj.getCanonicalName());
System.out.print("objectName:"+objname);
System.out.print(",最大會話數:"
+ mbsc.getAttribute(objname, "maxActiveSessions")+",");
System.out.print("會話數:"
+ mbsc.getAttribute(objname, "activeSessions")+",");
System.out.println("活動會話數:"
+ mbsc.getAttribute(objname, "sessionCounter"));
由於可以獲取的信息很多,暫不全部列出來
翻過頭來看下tomcat對JMX的支持
看完上面最後一段話,有沒有一種很雷人的感覺
關於tomcat對JMX的支持可以看下詳細的文檔
http://tomcat.apache.org/tomcat-6.0-doc/monitoring.html
當我們默認直接訪問tomcat提供的JMX接口時(http://localhost:8080/manager/jmxproxy/?qry=)會出現所有的MBeans,
如果想要具體的MBeans只需要將其name後面的值放在url的後面
因此,在默認情況下我們看到的信息都可以通過遠程方式獲取到,下一篇會介紹如何遠程管理Tomcat