公司要求实时监控服务器,写个Web的监控系统

转自:http://kakaluyi.iteye.com/blog/228636

公司的服务器需要实时监控,而且当用户空间已经满了,操作失败,或者出现程序Exception的时候就需要实时提醒,便于网管和程序员调式,这样就把这个实时监控系统分为了两部分,

第一部分:实时系统监控(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,一切大功告成,以下是最终效果

猜你喜欢

转载自marsvaadin.iteye.com/blog/1699021
今日推荐