JavaWeb的監控系統

Joker_Ye發表於2016-07-21

第一部分:實時系統監控(cpu利用率,cpu溫度,總記憶體大小,已使用記憶體大小)

第二部分:實時告警

由於無重新整理實時性,所以只能使用Ajax,這裡沒有用到任何ajax框架,因為呼叫比較簡單

大家知道,由於java的先天不足,對底層系統的呼叫和操作一般用jni來完成,特別是cpu溫度,你在window下是打死用命令列是得不到的,但由於我們的伺服器系統是linux,所以可以不呼叫jni完全用java的方式來得到系統資訊,這裡用到了runtime的exec()函式,通過解析本地命令呼叫的結果來查詢本地資訊,

這裡要感謝公司同事qinkun推薦ecsun兄的這篇文章http://papa.iteye.com/blog/220532

 

Java程式碼  收藏程式碼
  1. * 取得linux系統下的cpu、記憶體資訊   
  2. *   
  3. * */   
  4. public final class LinuxSystemTool   
  5. {   
  6. /**  
  7. * get memory by used info  
  8.  
  9. * @return int[] result  
  10. * result.length==4;int[0]=MemTotal;int[1]=MemFree;int[2]=SwapTotal;int[3]=SwapFree;  
  11. * @throws IOException  
  12. * @throws InterruptedException  
  13. */   
  14. public static int[] getMemInfo() throws IOException, InterruptedException   
  15. {   
  16. File file = new File("/proc/meminfo");   
  17. BufferedReader br = new BufferedReader(new InputStreamReader(   
  18. new FileInputStream(file)));   
  19. int[] result = new int[4];   
  20. String str = null;   
  21. StringTokenizer token = null;   
  22. while((str = br.readLine()) != null)   
  23. {   
  24. token = new StringTokenizer(str);   
  25. if(!token.hasMoreTokens())   
  26. continue;   
  27.   
  28. str = token.nextToken();   
  29. if(!token.hasMoreTokens())   
  30. continue;   
  31.   
  32. if(str.equalsIgnoreCase("MemTotal:"))   
  33. result[0] = Integer.parseInt(token.nextToken());   
  34. else if(str.equalsIgnoreCase("MemFree:"))   
  35. result[1] = Integer.parseInt(token.nextToken());   
  36. else if(str.equalsIgnoreCase("SwapTotal:"))   
  37. result[2] = Integer.parseInt(token.nextToken());   
  38. else if(str.equalsIgnoreCase("SwapFree:"))   
  39. result[3] = Integer.parseInt(token.nextToken());   
  40. }   
  41.   
  42. return result;   
  43. }   
  44.   
  45. /**  
  46. * get memory by used info  
  47.  
  48. * @return float efficiency  
  49. * @throws IOException  
  50. * @throws InterruptedException  
  51. */   
  52. public static float getCpuInfo() throws IOException, InterruptedException   
  53. {   
  54. File file = new File("/proc/stat");   
  55. BufferedReader br = new BufferedReader(new InputStreamReader(   
  56. new FileInputStream(file)));   
  57. StringTokenizer token = new StringTokenizer(br.readLine());   
  58. token.nextToken();   
  59. int user1 = Integer.parseInt(token.nextToken());   
  60. int nice1 = Integer.parseInt(token.nextToken());   
  61. int sys1 = Integer.parseInt(token.nextToken());   
  62. int idle1 = Integer.parseInt(token.nextToken());   
  63.   
  64. Thread.sleep(1000);   
  65.   
  66. br = new BufferedReader(   
  67. new InputStreamReader(new FileInputStream(file)));   
  68. token = new StringTokenizer(br.readLine());   
  69. token.nextToken();   
  70. int user2 = Integer.parseInt(token.nextToken());   
  71. int nice2 = Integer.parseInt(token.nextToken());   
  72. int sys2 = Integer.parseInt(token.nextToken());   
  73. int idle2 = Integer.parseInt(token.nextToken());   
  74.   
  75. return (float)((user2 + sys2 + nice2) - (user1 + sys1 + nice1)) / (float)((user2 + nice2 + sys2 + idle2) - (user1 + nice1 + sys1 + idle1));   
  76. }   
  77. }   

 

這裡的兩個方法,解釋一下,

方法1檔案"/proc/meminfo"裡面包含的就是記憶體的資訊,還包括了swap的資訊。例如: 

$ cat /proc/meminfo 

total: used: free: shared: buffers: cached: 
Mem: 1057009664 851668992 205340672 0 67616768 367820800 
Swap: 2146787328 164429824 1982357504 
MemTotal: 1032236 kB 
MemFree: 200528 kB 
MemShared: 0 kB 
這樣可以用擷取字串的方法,來得到linux記憶體資訊.

方法2在檔案"/proc/stat"裡面就包含了CPU的資訊。每一個CPU的每一tick用在什麼地方都在這個檔案裡面記著。後面的數字含義分別是: user、nice、sys、idle、iowait。有些版本的kernel沒有iowait這一項。這些數值表示從開機到現在,CPU的每tick用在了哪裡。例如: 

cpu0 256279030 0 11832528 1637168262 

就是cpu0從開機到現在有 256279030 tick用在了user消耗,11832528用在了sys消耗。所以如果想計算單位時間(例如1s)裡面CPU的負載,那隻需要計算1秒前後數值的差除以每一秒的tick數量就可以了。

ok這樣還剩下cpu溫度,怎麼做呢

發現了一個檔案"cat /proc/acpi/thermal_zone/THM/temperature";可以返回本機的linux溫度,

大概是這樣的:temperature:            68C

但不是每臺linux機器都有這個THM你要確定你的linux載入了這個THM才能使用這個檔案,這樣就用InputStreamReader(new FileInputStream(new File("/proc/acpi/thermal_zone/THM/temperature")),去讀取這個檔案,後面的相信大家一定會做了吧,就是把內容讀出來,然後分割字串去得到這個68。ok,系統基本資訊全部完成,然後ok現在就只有一件事就是用Ajax去呼叫這個類來得到 基本資訊,然後返回到頁面上,Ajax的用法就不贅言了。

 

下面是系統監控的效果,大概是Ajax每幾秒去linux下去取一次系統資訊,然後顯示在jsp頁面上,以下是效果。

 

 

 

 

到這裡第一部分系統監控部分已經完成,現在開始完成實時告警部分,分析需求

1溫度和cpu超過額定值需要告警

2使用者作業系統失敗,使用者儲存空間不足也需要告警,還有我們公司的業務操作失敗告警,如果發生Exception也只能告警,當然要把異常的堆疊的資訊儲存在資料庫裡,我就這樣設計如果使用者在操作中觸發了這些錯誤,則儲存在資料庫的告警表裡,然後實時監控的再取出來這些資訊。

3告警是要實時的那麼要怎麼從告警表裡查到當前以後的資料呢,一開始想到用當前時間,在當前時間加上Ajax傳送時間間隔,select * from warnlist where date>new Date()+AjaxTime這種形式,後來發現時間是很不正確的,網路延遲,程式處理時間,(cpu資訊用了sleep函式),等等你常常會發現有些告警資訊被無情的放過,而有的時候有重複資料,這樣我想到了用id,每次進入告警系統先查詢到最大的告警id,然後儲存在session中,然後ajax從資料庫裡取告警資訊的時候都查這個id之後的資料(就是進入監控系統後的最新資料),然後session再儲存新的最大id,下次ajax取還是從這個session中取最大id,這樣資訊就可以當ajax取的時候都保證是最新的,而且沒有重複,very good!就這樣做了

這樣設計了一張告警處理表

Sql程式碼  收藏程式碼
  1. CREATE TABLE `warnlist` (  
  2.   `Id` bigint(20) NOT NULL auto_increment,  
  3.   `warnleave` tinyint(2) NOT NULL default '0',//告警級別:告警的嚴重程度  
  4.   `fromguy` varchar(20) NOT NULL,//屬於哪個使用者哪個組織的告警  
  5.   `warncontent` varchar(100) NOT NULL,//告警內容,比如cpu使用率超過80%  
  6.   `aviliablevalue` varchar(12) default NULL,//允許值 比如85%  
  7.   `warnvalue` varchar(12) default NULL,//告警值 80  
  8.   `warntime` datetime NOT NULL,//告警時間  
  9.   `stackinfo` varchar(255) default NULL,//異常的堆疊資訊  
  10.   `dealwith` tinyint(2) NOT NULL default '0',//處理結果  
  11.   `version` int(11) default NULL,//version  
  12.   `organizerID` varchar(20) default NULL,//組織id  
  13.   `des` varchar(255) default NULL,  
  14.   PRIMARY KEY  (`Id`)  
  15. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;  

 

假設我ajax從系統取資訊後,那麼要寫個邏輯,if(cpuTempature>75C)or if(cpuUserd>80%)則寫入資料庫,然後再查詢大於上一次傳送Ajax資料庫的最大id的告警資訊(這期間如果發生的以下錯誤一併查出:使用者儲存空間不足,還有我們公司的業務操作失敗告警,Exception等),迴圈插入一個xml解析類中,大概形式是這樣的Ajax返回這個xml,供頁面提取資訊

Xml程式碼  收藏程式碼
  1. <response>  
  2. <cpuUsed>67</cpuUsed>  
  3. <cpuTemp>76<cpuTemp>  
  4. <Memory>1023422</Memory>  
  5. <freeMemory>43244</freeMemory>  
  6. <wannlist>  
  7. <warnid>2</warnid>  
  8. <warncontent>系統儲存空間不足</warncontent>  
  9. <fromguy>kakaluyi</fromguy>  
  10. ..............  
  11. </wanrlist>  
  12. <warnlist>  
  13. <warnid>3</warnid>  
  14. <warncontent>cpu溫度過高</warncontent>  
  15. <fromguy>系統</fromguy>  
  16. <orgid>系統</orgid>  
  17. <warnvalue>78</warnvalue>  
  18. .............  
  19. </warnlist>  
  20. ........  
  21.   
  22. </response>  

 

系統資訊的顯示程式碼,就是關聯上面那個圖片的:

Html程式碼  收藏程式碼
  1. var cpuUsed=req.responseXML.getElementsByTagName('cpuUsed')[0].firstChild.nodeValue;  
  2. var totalMemory=req.responseXML.getElementsByTagName('totalMemory')[0].firstChild.nodeValue;  
  3. var freeMemory=req.responseXML.getElementsByTagName('freeMemory')[0].firstChild.nodeValue;  
  4. var cpuTemp=req.responseXML.getElementsByTagName('cpuTemp')[0].firstChild.nodeValue;  
  5. $('cpuUsed').innerHTML=cpuUsed;  
  6. $('totalMemory').innerHTML=totalMemory;  
  7. $('freeMemory').innerHTML=freeMemory;  
  8. $('cpuTemp').innerHTML=cpuTemp;  
  9.   
  10. //jsp  
  11. <tr>  
  12. <td class="label" width="20%">  
  13. 伺服器CPU使用率:</td>  
  14. <td class="text">  
  15. <font color="#FF0000" size="+2"><label id="cpuUsed"></label>  
  16. </font> < 告警預定閥值: 80% >  
  17. </td>  
  18. </tr>  
  19.  .........  

然後就是頁面展現的問題了這裡我用了dom節點的增刪,一個頁面保持50條記錄,如果超過50條則刪除以前的節點,程式碼為:

 

 

Js程式碼  收藏程式碼
  1. var length=req.responseXML.getElementsByTagName('warnlist').length;  
  2. if(length>0)  
  3. {  
  4. var trlength=document.getElementsByTagName('table')[4].childNodes[0].childNodes.length;  
  5.   
  6. if(trlength+length-1>50)//如果大於50條,則查詢告警列表的table,得到  
  7. 告警資訊的子節點,然後刪除多餘的最早的告警資訊  
  8. {  
  9. var tbody=document.getElementsByTagName('table')[4].childNodes[0];  
  10. for(var i=1;i<trlength+length-50;i++)  
  11. {  
  12. var tr=tbody.childNodes[i];  
  13. tr.parentNode.removeChild(tr);  
  14.   
  15. }  

 

然後插入新的告警資訊,

Js程式碼  收藏程式碼
  1. for(var i=0;i<length;i++)  
  2. {  
  3. var onewarnlist=req.responseXML.getElementsByTagName('warnlist')[i].childNodes;  
  4. if(onewarnlist[0].firstChild.nodeValue==0)  
  5. {  
  6. var leave="企業級告警";  
  7. }  
  8. else {  
  9. var leave="運營商級告警";  
  10. }  
  11. var from=onewarnlist[1].firstChild.nodeValue;  
  12. var warncontent=onewarnlist[2].firstChild.nodeValue;  
  13. var aviliablevalue=onewarnlist[3].firstChild.nodeValue;  
  14. var warnvalue=onewarnlist[4].firstChild.nodeValue;  
  15. var warntime=onewarnlist[5].firstChild.nodeValue;  
  16. var id=onewarnlist[8].firstChild.nodeValue;  
  17. if(onewarnlist[6].firstChild.nodeValue==0)  
  18. {  
  19. var dealwith="未處理" ;  
  20. }  
  21. else {  
  22. var dealwith="<font color='red'>已處理</font>";  
  23. }  
  24. var table=document.getElementById('warntable');  
  25. var tr=document.createElement('tr');  
  26.  if(x%2==1)  
  27. {  
  28. tr.style.backgroundColor="#BFD3F9"  
  29. }  
  30. else{  
  31. tr.style.backgroundColor="#FBFCEB"  
  32. }  
  33. x++;  
  34. table.appendChild(tr);  
  35. var td=document.createElement('td');  
  36. td.className ='listText';  
  37. td.innerHTML =x;  
  38. tr.appendChild(td);  
  39. var td1=document.createElement('td');  
  40. td1.className ='listText';  
  41. td1.innerHTML = leave;  
  42. tr.appendChild(td1);  
  43. var td2=document.createElement('td');  
  44. td2.className ='listText';  
  45. td2.innerHTML = from;  
  46. tr.appendChild(td2);  
  47. var td3=document.createElement('td');  
  48. td3.className ='listText';  
  49. td3.innerHTML = warncontent;  
  50. tr.appendChild(td3);6  
  51. var td4=document.createElement('td');  
  52. td4.className ='listText';  
  53. td4.innerHTML = aviliablevalue;  
  54. tr.appendChild(td4);  
  55. var td5=document.createElement('td');  
  56. td5.className ='listText';  
  57. td5.innerHTML = '<font color="#FF0000">'+warnvalue+'</font>';  
  58. tr.appendChild(td5);  
  59. var td6=document.createElement('td');  
  60. td6.className ='listText';  
  61. td6.innerHTML = warntime;  
  62. tr.appendChild(td6);  
  63. var td7=document.createElement('td');  
  64. td7.className ='listText';  
  65. td7.innerHTML = dealwith;  
  66. tr.appendChild(td7);  
  67. var td8=document.createElement('td');  
  68. td8.className ='listText';  
  69. td8.innerHTML = id;  
  70. tr.appendChild(td8);  
  71.    }  

 

ok,一切大功告成,以下是最終效果

相關文章