• JavaWeb的监控系统


    第一部分:实时系统监控(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.   
    2. 67  
    3. 76  
    4. 1023422  
    5. 43244  
    6.   
    7. 2  
    8. 系统存储空间不足  
    9. kakaluyi  
    10. ..............  
    11.   
    12.   
    13. 3  
    14. cpu温度过高  
    15. 系统  
    16. 系统  
    17. 78  
    18. .............  
    19.   
    20. ........  
    21.   
    22.   

    系统信息的显示代码,就是关联上面那个图片的:

    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.   
    12.   
    13. 服务器CPU使用率:  
    14.   
    15.   
    16.  < 告警预定阀值: 80% >  
    17.   
    18.   
    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
    11. {  
    12. var tr=tbody.childNodes[i];  
    13. tr.parentNode.removeChild(tr);  
    14.   
    15. }  

    然后插入新的告警信息,

    Js代码   

    1. for(var i=0;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="已处理";  
    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 = ''+warnvalue+'';  
    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.    }  
  • 相关阅读:
    【C++】详细讲解函数使用,带你玩转C++函数~
    .NET宝藏API之:IHostedService,后台任务执行
    UE4 Sequence添加基础动画效果 (04-在序列中使用粒子效果)
    Qt+WebAssembly学习笔记(六)win10+Qt6.4.0rc1开发环境搭建
    <C++> STL_bitset使用和模拟实现
    java8函数式编程之Stream流处理的方法和案例讲解
    大厂秋招真题【模拟】阿里蚂蚁20231010秋招T2-奇偶操作
    JDK新特性-Stream流
    linux驱动39:内存池
    Spring Boot 2.x系列【17】功能篇之JSON
  • 原文地址:https://blog.csdn.net/m0_62089210/article/details/126611670