java怎么做实时监控系统?简单的web监控系统

我们之前说过,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页面上,以下是效果:

java怎么做实时监控系统?简单的web监控系统.png

实时告警部分,分析需求:

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工具网。

推荐阅读:

java二叉树怎么实现?简单二叉树实现

java如何做到同时执行?多个线程同时执行方法

json文件怎么转换成excel文件?如何打开json文件?