使用Perl实现系统服务监控和报警 - re_think的专栏 - CSDN博客

来源:百度文库 编辑:神马文学网 时间:2024/04/28 11:41:27
使用Perl实现系统服务监控和报警 收藏 作者:heiyeluren
博客:http://blog.csdn.net/heiyeshuwu
时间:2008-5-22
一般的Web站点来说,都包括很多服务和应用,我们没法实时知道系统运行是否正常,特别是晚上的时候,如果服务器宕机或应用挂掉了,都会影响业务和用户访问,这时候一套对系统监控的错设就必须得当。目前有很多软件应的监控通知和报警服务,有收费的也有免费的,大家都可以选择。我们就尝试自己来实现一个服务监控和报警通知的程序,这样能够使用很小的代价,同样让我们的服务高可用性和高可靠性。
【监控原理】 
远程服务对于远程机器来说,我们可以有一台监控服务器,或者随便找一台比较不容宕机的服务器来作为监控服务器,那么就能够监控其他的服务机上的服务了,远程监控是比较大家需要的方式了。一般远程监控就监控服务器和端口是否开放,比如说,我们的 Web 服务 Apache 一般都会开放 80 端口,那么我们就可以通过访问这台服务器的 80 端口来确定 Apache 是否在正常工作,如果无法连接上,那么说明该服务就停止了。本地服务
对于本机来说,监控进程和日志文件都是可行的,一般来说,长期频繁工作的服务,比如 Apache 都会在每次访问后把访问信息记录到 access 访问日志文件里,如果这个文件长时间没有更新,就可以怀疑该服务已经停止了(当然了,不排除,这段时间内都没有人访问的情况)。另外对于进程来说,本机是很容易可以查看到进程情况的,对于 MySQL 等服务器来说,守护进程都是长期开放的,如果发现当前系统中没有了 MySQL 守护进程,那么也可以确认 MySQL 服务已经停止了。 报警通知
 服务停止了,自然需要通知系统维护人员,那么一般就是通过邮件或者短信的方式,短信是最好的了,但是频繁短信同样让维护人员很郁闷,这个叫做短信炸弹(Message Bomb),所以邮件也许是个简单实在的方式,本地再配置上 Outlook/Foxmail 定期接收和通知方式,也比较快捷,但是晚上回家后,一般都无法收到邮件了,所以合理的方式是白天邮件通知,晚上和周末短信通知 的报警方式更合理。【代码实现】具体代码实现可以使用各种代码了,C/C++、Ruby、Python、PHP ,只要能够访问文件、Socket ,能够定期执行的语言都可以,我们下面的代码采用 Perl 来构建,因为 Perl 是很好的系统管理脚本语言,任何 Unix/Linux 都缺省安装了 Perl 引擎,能够很方便的在任何机器上面运行,同时 Perl 的灵活性强,而且有强大的 CPAN 包库,所以编写代码很方便,在系统管理中也是值得推荐大家使用的,当然了,很多系统管理工作使用 shell 脚本也许更方便。下面的代码实现对远程监控、本地日志文件监控、本地进程监控都进行了实现,但是只使用了远程端口监控的方式,因为这样就能够监控多台机器和服务了,如果只是单台机器或者只是想监控本地进程,可以很简单的修改主函数来实现。同时通知方式主要是采用邮件通知的方式,并且函数实现了SMTP协议进行邮件发送(因为我发现Perl内置的 Net::SMTP 在进行型验证的时候,并不是很靠谱),当然了,在报警通知方面,完全可以改写成发送短信或者按照时间来分别调用短信和邮件的方式。代码中主要监控了包括  Apache、MySQL、Memcache、Search(假如你有的话)等服务,可以在这个基础上进行增删不同的服务器监控,只需要增加一个常量配置和修改 main 函数代码。说明:以下Perl代码在 RHEL 4 + Perl v5.8.6 环境下测试通过#!/usr/bin/perl
use IO::Socket;
use IO::File;
use MIME::Base64;##############################
# Constant define (configure)
##############################
# mail config
use constant MAIL_ADDR          => ('to'=>'webmaster@example.com', 'from'=>'webmaster@example.com');
use constant SMTP_INFO          => ('host'=>'smtp.example.com', 'user'=>'webmaster', 'password'=>'pass',
                                    'debug'=>1, 'bufsize'=>1024);
# common config
use constant MD5SUM_FILE        => '/tmp/__monitor_md5sum_hash';
use constant APACHE_LOG_PATH    => '/usr/local/apache2/logs/access';
# apache
use constant APACHE_PORT        => 80;
use constant APACHE_SERVERS     => ('web1.example.com', 'web2.example.com');
# mysql
use constant MYSQL_PORT         => 3306;
use constant MYSQL_SERVERS      => ('db1.example.com', 'db2.example.com');
# memcache
use constant MEMCACHE_PORT      => 11211;
use constant MEMCACHE_SERVERS   => ('cache1.example.com', 'cache2.example.com');
# search
use constant SEARCH_PORT        => 8000;
use constant SEARCH_SERVERS     => ('search1.example.com');
##############################
# Server port is alive check
##############################
sub check_server_alive {
    my($server, $port) = @_;    $sock = IO::Socket::INET->new(PeerAddr=>$server, PeerPort=>$port, Proto=>'tcp', Timeout=>3);
    if (!$sock){
        return 0;
    }
    $sock->close();
    return 1;
}##############################
# Check process is exist
##############################
sub check_process_exist {
    my $proc_name = shift;
    $line = `/bin/ps auxw | /bin/grep $proc_name | /bin/grep -v grep | /usr/bin/wc -l`;
    $line =~ s/^s+|s+$//g;
    if ($line == 0){
        return 0;
    }
    return 1;
}##############################
# Check file md5 fingerprint
##############################
sub check_file_md5sum {
    my $io, $line;
    $filename = shift;
    @arr = split(/s/, `/usr/bin/md5sum $filename`);
    $filehash = shift(@arr);
    $io = IO::File->new();
    $io->open(MD5SUM_FILE, O_RDWR);
    if (!($line = $io->getLine())){
        $io->syswrite($filehash);
        $io->close;
        return true;
    }
    if ($line != $filehash){
        $io->truncate(0);
        $io->syswrite($filehash);
        $io->close;
        return true;
    }
    return true;
}##############################
# SMTP execute command
##############################
sub smtp_cmd {
    my ($sock, $cmd, $blocking) = @_;
    my %smtpinfo = SMTP_INFO;
    my $buf, $bufsize = $smtpinfo{'bufsize'}, $debug=$smtpinfo{'debug'};    $sock->syswrite($cmd);
    if ($debug == 1){
        print ">>> $cmd ";
    }
    if ($blocking == 1){
        $sock->sysread($buf, $bufsize);
        if ($debug){
            print "<<< $buf";
        }
    }
}##############################
# Send notice mail
##############################
sub send_mail {
    my ($subject, $content) = @_;
    my $sock;
    my %mailaddr = MAIL_ADDR;
    my %smtpinfo = SMTP_INFO;
    my $debug = $smtpinfo{'debug'};    # Count date time
    ($sec, $min, $hour, $day, $mon, $year, $wday, $yday, $isdst) = localtime(time());
    $datetime = sprintf("%s-%s-%s %s:%s:%s", "20".substr($year,1,2), length($mon)==1?"0$mon":$mon, length($day)==1?"0$day":$day, length($hour)==1?"0$hour":$hour, length($min)==1?"0$min":$min, length($sec)==1?"0$sec":$sec);
    $subject .= "[$datetime]";    # Connect to SMTP server
    $sock = IO::Socket::INET->new(PeerAddr=>$smtpinfo{'host'}, PeerPort=>25, Proto=>'tcp', Timeout=>10);
    $sock->blocking(1);    # Send smtp command
    if ($debug == 1){
        print "<<< ". $sock->sysread($buf, $smtpinfo{'bufsize'});
    }
    smtp_cmd($sock, "HELO locahost ", 1);
    smtp_cmd($sock, "AUTH LOGIN ", 1);
    smtp_cmd($sock, encode_base64($smtpinfo{'user'}), 1);
    smtp_cmd($sock, encode_base64($smtpinfo{'password'}), 1);
    smtp_cmd($sock, "MAIL FROM: <". $mailaddr{'from'} ."> ", 1);
    smtp_cmd($sock, "RCPT TO: <". $mailaddr{'to'} ."> ", 1);
    smtp_cmd($sock, "DATA ", 1);
    smtp_cmd($sock, "From: ". $smtpinfo{'from'} ." ", 0);
    smtp_cmd($sock, "To: ". $smtpinfo{'to'} ." ", 0);
    smtp_cmd($sock, "Subject: $subject ", 0);
    smtp_cmd($sock, "$content ", 0);
    smtp_cmd($sock, " . ", 1);
    smtp_cmd($sock, "QUIT ", 0);
    $sock->close();    return 1;
}##############################
# Check server alive main
##############################
sub monitor_main {
    # check apache
    foreach $item (APACHE_SERVERS) {
        if (!check_server_alive($item, APACHE_PORT)) {
            send_mail("$item apache server is down", "$item apache server is down. please timely restoration");
        }
    }
    # check mysql
    foreach $item (MYSQL_SERVERS) {
        if (!check_server_alive($item, MYSQL_PORT)) {
            send_mail("$item mysql server is down", "$item mysql server is down. please timely restoration");
        }
    }
    # check memcache
    foreach $item (MEMCACHE_SERVERS) {
        if (!check_server_alive($item, MEMCACHE_PORT)) {
            send_mail("$item memcache server is down", "$item memcache server is down. please timely restoration");
        }
    }
    # check search
    foreach $item (SEARCH_SERVERS) {
        if (!check_server_alive($item, SEARCH_PORT)) {
            send_mail("$item search server is down", "$item search server is down. please timely restoration");
        }
    }
}
##############################
# Main running
##############################monitor_main();
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/re_think/archive/2009/06/25/4296833.aspx
使用Perl实现系统服务监控和报警 - re_think的专栏 - CSDN博客 使用NGiNX_HTTP_Push_Module实现简单的服务器推送 - hfahe的专栏 - CSDN博客 在C#中使用COM+实现事务控制 - zgqtxwd的专栏 - CSDN博客 系统架构师的修炼 - welcomejzh的专栏 - CSDN博客 输入/输出系统 - hbrqlpf的专栏 - CSDN博客 STL之vector的使用 - wxdvc的专栏 - CSDN博客 使用自己的make menuconfig - Hermit的专栏 - CSDN博客 awk使用教程 - elijah5748的专栏 - CSDN博客 Ubuntu8.04简单使用笔记 - maojudong的专栏 - CSDN博客 Xshell使用小结 - nsj820的专栏 - CSDN博客 LINUX环境下使用CVS - jiahehao的专栏 - CSDN博客 baozhengw的专栏 - CSDN博客 采用部分快速排序算法实现数组的部分排序 - eaglet的专栏 - CSDN博客 Oracle存储过程实现多线程对表数据的抽取 - 狮子尾巴的专栏 - CSDN博客 C#实现启用、禁用本地网络的三种方式 - dsd999的专栏 - CSDN博客 C#实现启用、禁用本地网络的三种方式 - dsd999的专栏 - CSDN博客 Android图形架构实现分析总结 - shenbin1430的专栏 - CSDN博客 说说大型高并发高负载网站的系统架构 - elimago的专栏 - CSDN博客 继承与混合,略谈系统的构建方式 - aimingoo的专栏 - CSDN博客 嵌入式系统与普适计算 - pdcc的专栏 - CSDN博客 用Boost.Python构建混合系统 - 金庆的专栏 - CSDN博客 从一段对话也看强类型系统 - wangxia0557的专栏 - CSDN博客 可变长度函数参数的原理及使用 - shenbin1430的专栏 - CSDN博客 在基于对话框程序中使用WM_KICKIDLE消息 - ruo_gu的专栏 - CSDN博客