zoukankan      html  css  js  c++  java
  • 使用Java编写一个简单的Web的监控系统cpu利用率,cpu温度,总内存大小

    原文:http://www.jb51.net/article/75002.htm

    这篇文章主要介绍了使用Java编写一个简单的Web的监控系统的例子,并且将重要信息转为XML通过网页前端显示,非常之实用,需要的朋友可以参考下

    公司的服务器需要实时监控,而且当用户空间已经满了,操作失败,或者出现程序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页面上,以下是效果。

    20151117154830520.png (450×204)

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

    20151117155058639.png (762×343)

  • 相关阅读:
    MyBatis学习总结——MyBatis快速入门
    Java网络socket编程详解
    java中导出EXCEL表格
    利用反射将xml转换为List<Object>
    Java中将xml转为List<实体类>
    JavaWeb | 之 | 角色管理的表结构设计和原理
    fastjson把对象转化成json避免$ref
    JSON解析(DATE)对象数据
    jstl中<c:forEach>的用法
    请求转发与请求重定向的区别
  • 原文地址:https://www.cnblogs.com/shihaiming/p/6114382.html
Copyright © 2011-2022 走看看