我们之前说过,java的应用领域非常广,不仅可以做游戏,还可以开发网页,当然也是可以做实时监控系统,那java怎么做实时监控系统?下面来我们就用实例来讲解一下。
第一部分:实时系统监控(cpu利用率,cpu温度,总内存大小,已使用内存大小)。
第二部分:实时告警。
由于无刷新实时性,所以只能使用Ajax,这里没有用到任何ajax框架,因为调用比较简单
大家知道,由于java的先天不足,对底层系统的调用和操作一般用jni来完成,特别是cpu温度,你在window下是打死用命令行是得不到的, 但由于我们的服务器系统是linux,所以可以不调用jni完全用java的方式来得到系统信息,这里用到了runtime的exec()函数,通过解析 本地命令调用的结果来查询本地信息。
* 取得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)); } }
下面是系统监控的效果,大概是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取的时候都保证是最新的,而且没有重复,就这样做了。
这样设计了一张告警处理表
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 < cpuTemp > 76 < cpuTemp > < Memory > 1023422 < freeMemory > 43244 < wannlist > < warnid > 2 < warncontent > 系统存储空间不足 < fromguy > kakaluyi .............. < warnlist > < warnid > 3 < warncontent > cpu温度过高 < fromguy > 系统 < orgid > 系统 < warnvalue > 78 ............. ........
系统信息的显示代码,就是关联上面那个图片的:
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 class = "text" > < font color = "#FF0000" size = "+2" > < label id = "cpuUsed" > < 告警预定阀值: 80 % > .........
然后就是页面展现的问题了这里我用了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++) < p = "" > { var tr = tbody.childNodes[i]; tr.parentNode.removeChild(tr); }
然后插入新的告警信息,
for (var i = 0; i < length; i++) < p = "" > { 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 = "已处理"; } var table = document.getElementById('warntable'); var tr = document.createElement('tr'); if (x % 2 == 1) { tr.style.backgroundColor = "#BFF9" } 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 = '' + warnvalue + ''; 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); }
这样一切大功告成,虽然说做监控系统需要的逻辑很多,但只要将每一个功能用代码表现出来就完成了,最后大家如果想要了解更多java实例知识,敬请关注奇Q工具网。
推荐阅读: