精华帖 (0) :: 良好帖 (7) :: 新手帖 (0) :: 隐藏帖 (2)
|
|
---|---|
作者 | 正文 |
发表时间:2008-08-16
最后修改:2009-04-14
公司的服务器需要实时监控,而且当用户空间已经满了,操作失败,或者出现程序Exception的时候就需要实时提醒,便于网管和程序员调式,这样就把这个实时监控系统分为了两部分,
第一部分:实时系统监控(cpu利用率,cpu温度,总内存大小,已使用内存大小) 第二部分:实时告警 由于无刷新实时性,所以只能使用Ajax,这里没有用到任何ajax框架,因为调用比较简单 大家知道,由于java的先天不足,对底层系统的调用和操作一般用jni来完成,特别是cpu温度,你在window下是打死用命令行是得不到的,但由于我们的服务器系统是linux,所以可以不调用jni完全用java的方式来得到系统信息,这里用到了runtime的exec()函数,通过解析本地命令调用的结果来查询本地信息, 这里要感谢公司同事qinkun推荐ecsun兄的这篇文章http://papa.iteye.com/blog/220532,
* 取得linux系统下的cpu、内存信息 * * */ public final class LinuxSystemTool { /** * get memory by used info * * @return int[] result * result.length==4;int[0]=MemTotal;int[1]=MemFree;int[2]=SwapTotal;int[3]=SwapFree; * @throws IOException * @throws InterruptedException */ public static int[] getMemInfo() throws IOException, InterruptedException { File file = new File("/proc/meminfo"); BufferedReader br = new BufferedReader(new InputStreamReader( new FileInputStream(file))); int[] result = new int[4]; String str = null; StringTokenizer token = null; while((str = br.readLine()) != null) { token = new StringTokenizer(str); if(!token.hasMoreTokens()) continue; str = token.nextToken(); if(!token.hasMoreTokens()) continue; if(str.equalsIgnoreCase("MemTotal:")) result[0] = Integer.parseInt(token.nextToken()); else if(str.equalsIgnoreCase("MemFree:")) result[1] = Integer.parseInt(token.nextToken()); else if(str.equalsIgnoreCase("SwapTotal:")) result[2] = Integer.parseInt(token.nextToken()); else if(str.equalsIgnoreCase("SwapFree:")) result[3] = Integer.parseInt(token.nextToken()); } return result; } /** * get memory by used info * * @return float efficiency * @throws IOException * @throws InterruptedException */ public static float getCpuInfo() throws IOException, InterruptedException { File file = new File("/proc/stat"); BufferedReader br = new BufferedReader(new InputStreamReader( new FileInputStream(file))); StringTokenizer token = new StringTokenizer(br.readLine()); token.nextToken(); int user1 = Integer.parseInt(token.nextToken()); int nice1 = Integer.parseInt(token.nextToken()); int sys1 = Integer.parseInt(token.nextToken()); int idle1 = Integer.parseInt(token.nextToken()); Thread.sleep(1000); br = new BufferedReader( new InputStreamReader(new FileInputStream(file))); token = new StringTokenizer(br.readLine()); token.nextToken(); int user2 = Integer.parseInt(token.nextToken()); int nice2 = Integer.parseInt(token.nextToken()); int sys2 = Integer.parseInt(token.nextToken()); int idle2 = Integer.parseInt(token.nextToken()); return (float)((user2 + sys2 + nice2) - (user1 + sys1 + nice1)) / (float)((user2 + nice2 + sys2 + idle2) - (user1 + nice1 + sys1 + idle1)); } }
这里的两个方法,解释一下, 方法1文件"/proc/meminfo"里面包含的就是内存的信息,还包括了swap的信息。例如: 方法2在文件"/proc/stat"里面就包含了CPU的信息。每一个CPU的每一tick用在什么地方都在这个文件里面记着。后面的数字含义分别是: user、nice、sys、idle、iowait。有些版本的kernel没有iowait这一项。这些数值表示从开机到现在,CPU的每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!就这样做了 这样设计了一张告警处理表 CREATE TABLE `warnlist` ( `Id` bigint(20) NOT NULL auto_increment, `warnleave` tinyint(2) NOT NULL default '0',//告警级别:告警的严重程度 `fromguy` varchar(20) NOT NULL,//属于哪个用户哪个组织的告警 `warncontent` varchar(100) NOT NULL,//告警内容,比如cpu使用率超过80% `aviliablevalue` varchar(12) default NULL,//允许值 比如85% `warnvalue` varchar(12) default NULL,//告警值 80 `warntime` datetime NOT NULL,//告警时间 `stackinfo` varchar(255) default NULL,//异常的堆栈信息 `dealwith` tinyint(2) NOT NULL default '0',//处理结果 `version` int(11) default NULL,//version `organizerID` varchar(20) default NULL,//组织id `des` varchar(255) default NULL, PRIMARY KEY (`Id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
假设我ajax从系统取信息后,那么要写个逻辑,if(cpuTempature>75C)or if(cpuUserd>80%)则写入数据库,然后再查询大于上一次发送Ajax数据库的最大id的告警信息(这期间如果发生的以下错误一并查出:用户存储空间不足,还有我们公司的业务操作失败告警,Exception等),循环插入一个xml解析类中,大概形式是这样的Ajax返回这个xml,供页面提取信息 <response> <cpuUsed>67</cpuUsed> <cpuTemp>76<cpuTemp> <Memory>1023422</Memory> <freeMemory>43244</freeMemory> <wannlist> <warnid>2</warnid> <warncontent>系统存储空间不足</warncontent> <fromguy>kakaluyi</fromguy> .............. </wanrlist> <warnlist> <warnid>3</warnid> <warncontent>cpu温度过高</warncontent> <fromguy>系统</fromguy> <orgid>系统</orgid> <warnvalue>78</warnvalue> ............. </warnlist> ........ </response>
系统信息的显示代码,就是关联上面那个图片的: var cpuUsed=req.responseXML.getElementsByTagName('cpuUsed')[0].firstChild.nodeValue; var totalMemory=req.responseXML.getElementsByTagName('totalMemory')[0].firstChild.nodeValue; var freeMemory=req.responseXML.getElementsByTagName('freeMemory')[0].firstChild.nodeValue; var cpuTemp=req.responseXML.getElementsByTagName('cpuTemp')[0].firstChild.nodeValue; $('cpuUsed').innerHTML=cpuUsed; $('totalMemory').innerHTML=totalMemory; $('freeMemory').innerHTML=freeMemory; $('cpuTemp').innerHTML=cpuTemp; //jsp <tr> <td class="label" width="20%"> 服务器CPU使用率:</td> <td class="text"> <font color="#FF0000" size="+2"><label id="cpuUsed"></label> </font> < 告警预定阀值: 80% > </td> </tr> ......... 然后就是页面展现的问题了这里我用了dom节点的增删,一个页面保持50条记录,如果超过50条则删除以前的节点,代码为:
var length=req.responseXML.getElementsByTagName('warnlist').length; if(length>0) { var trlength=document.getElementsByTagName('table')[4].childNodes[0].childNodes.length; if(trlength+length-1>50)//如果大于50条,则查找告警列表的table,得到 告警信息的子节点,然后删除多余的最早的告警信息 { var tbody=document.getElementsByTagName('table')[4].childNodes[0]; for(var i=1;i<trlength+length-50;i++) { var tr=tbody.childNodes[i]; tr.parentNode.removeChild(tr); }
然后插入新的告警信息, for(var i=0;i<length;i++) { var onewarnlist=req.responseXML.getElementsByTagName('warnlist')[i].childNodes; if(onewarnlist[0].firstChild.nodeValue==0) { var leave="企业级告警"; } else { var leave="运营商级告警"; } var from=onewarnlist[1].firstChild.nodeValue; var warncontent=onewarnlist[2].firstChild.nodeValue; var aviliablevalue=onewarnlist[3].firstChild.nodeValue; var warnvalue=onewarnlist[4].firstChild.nodeValue; var warntime=onewarnlist[5].firstChild.nodeValue; var id=onewarnlist[8].firstChild.nodeValue; if(onewarnlist[6].firstChild.nodeValue==0) { var dealwith="未处理" ; } else { var dealwith="<font color='red'>已处理</font>"; } var table=document.getElementById('warntable'); var tr=document.createElement('tr'); if(x%2==1) { tr.style.backgroundColor="#BFD3F9" } else{ tr.style.backgroundColor="#FBFCEB" } x++; table.appendChild(tr); var td=document.createElement('td'); td.className ='listText'; td.innerHTML =x; tr.appendChild(td); var td1=document.createElement('td'); td1.className ='listText'; td1.innerHTML = leave; tr.appendChild(td1); var td2=document.createElement('td'); td2.className ='listText'; td2.innerHTML = from; tr.appendChild(td2); var td3=document.createElement('td'); td3.className ='listText'; td3.innerHTML = warncontent; tr.appendChild(td3);6 var td4=document.createElement('td'); td4.className ='listText'; td4.innerHTML = aviliablevalue; tr.appendChild(td4); var td5=document.createElement('td'); td5.className ='listText'; td5.innerHTML = '<font color="#FF0000">'+warnvalue+'</font>'; tr.appendChild(td5); var td6=document.createElement('td'); td6.className ='listText'; td6.innerHTML = warntime; tr.appendChild(td6); var td7=document.createElement('td'); td7.className ='listText'; td7.innerHTML = dealwith; tr.appendChild(td7); var td8=document.createElement('td'); td8.className ='listText'; td8.innerHTML = id; tr.appendChild(td8); }
ok,一切大功告成,以下是最终效果 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2008-08-16
自己才做了快一年的java,
平时就写写基本的增删改查,这个模块是第一次有别于自己已往做得,所以比较兴奋, 写一下,也便于记录自己的设计过程,我的dom操作,ajax不是很精通就上阵了,做得这个模块只是beta版本,肯定有很多不足,希望老鸟们提点意见 |
|
返回顶楼 | |
发表时间:2008-08-16
我实习的时候也做过类似的玩意儿。远程监控30多台计算节点的CPU信息,内存信息,远程启动特定应用(带命令行交互)
是些Socket+JNI |
|
返回顶楼 | |
发表时间:2008-08-16
引用 我实习的时候也做过类似的玩意儿。远程监控30多台计算节点的CPU信息,内存信息,远程启动特定应用(带命令行交互)
是些Socket+JNI 30多个计算机节点用socket通信,返回值统一管理,原理应该是一样的,但用jni肯定比我的好,毕竟像我用解析命令行返回值是比较无奈的方法,一开始想像你一样用到的jni,可是要用到c,c++的动态链接库,实在不会(我是理科非计算机毕业的,就学过半年c),没办法,还好就监控一台机器,效率不高系统还运行的比较稳定。 |
|
返回顶楼 | |
发表时间:2008-08-18
看来大家很少需要做这种实时监控,或者javaeye大牛真的很多,
觉得鄙人做得东东档次太低,不屑给建议,曲低和寡啊。。。 |
|
返回顶楼 | |
发表时间:2008-08-18
不错,学习了.其实像这个应用,不用AJAX也可以,直接用XMLHTTP.
|
|
返回顶楼 | |
发表时间:2008-08-18
回楼上,多谢回复
不过你说不用Ajax而用xmlhttp应该这句话值得商榷吧, 据我所知,XMLHTTP就是Ajax的组成部分吧 |
|
返回顶楼 | |
发表时间:2008-08-18
我以前做过一个项目里面涉及过这方面的需求是采用SNMP实现的。LZ不妨参考下SNMP相关的资料
|
|
返回顶楼 | |
发表时间:2008-08-19
可以参考一下jsnmp或snmp4j
|
|
返回顶楼 | |
发表时间:2008-08-19
适合学习 玩玩 但不实用 程序设计比较有创意 让我回想起很久以前设计的IP语音会议的实时监控 原理差不多 但是Java毕竟是Java 后来我们的程序改用Qt写了 祝你好运。
|
|
返回顶楼 | |