欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

使用Java编写一个简单的Web的监控系统

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

公司的服务器需要实时监控,而且当用户空间已经满了,操作失败,或者出现程序exception的时候就需要实时提醒,便于网管和程序员调式,这样就把这个实时监控系统分为了两部分,
 
第一部分:实时系统监控(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));  
}  
}  

 
这里的两个方法,解释一下,
方法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页面上,以下是效果。

使用Java编写一个简单的Web的监控系统

到这里第一部分系统监控部分已经完成,现在开始完成实时告警部分,分析需求
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,一切大功告成,以下是最终效果

使用Java编写一个简单的Web的监控系统