我为我们的服务器编写了一个非常简单的正常运行时间监视器(很好地从其他人的代码中拼凑而来)-它只是一个ICMP(ping)监视器,它对我们数量有限的服务器(大约20台)运行得非常好,而且非常快。以下是代码(我认为实际的ping测试函数是基于Birk Jensen的工作(http://birk-jensen.dk/2010/09/php-ping/),我刚刚利用他的功能在一切正常时显示一个绿色圆圈PNG,在每台服务器出现故障时显示红色圆圈(如果有的话)。
<html>
<head>
<style type='text/css'>
*{
font-family:verdana,tahoma,arial;
font-size:17px;
}
.light{width:30px;}
h1{
font-size:25px;
}
</style>
<meta http-equiv="refresh" content="30">
</head>
<body>
<?php
$time1=date('H:i:s');
echo "Last Refresh Time = $time1<br/><hr/>";
error_reporting(0);
/*-----------------------------------------------------------------------------------------*/
// Checksum calculation function
function icmpChecksum($data)
{
if (strlen($data)%2)
$data .= "x00";
$bit = unpack('n*', $data);
$sum = array_sum($bit);
while ($sum >> 16)
$sum = ($sum >> 16) + ($sum & 0xffff);
return pack('n*', ~$sum);
}
/*-----------------------------------------------------------------------------------------*/
function PingTry1($pingaddress){
// Making the package
$type= "x08";
$code= "x00";
$checksum= "x00x00";
$identifier = "x00x00";
$seqNumber = "x00x00";
$data= "testing123";
$package = $type.$code.$checksum.$identifier.$seqNumber.$data;
$checksum = icmpChecksum($package); // Calculate the checksum
$package = $type.$code.$checksum.$identifier.$seqNumber.$data;
// And off to the sockets
$socket = socket_create(AF_INET, SOCK_RAW, 1);
socket_set_option ( $socket, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>1, "usec"=>0) );
socket_connect($socket, $pingaddress, null);
$startTime = microtime(true);
socket_send($socket, $package, strLen($package), 0);
if (socket_read($socket, 255)) {
return true;
}
else{
return false;
}
socket_close($socket);
}
/*-----------------------------------------------------------------------------------------*/
function DoTheCheck($name,$ip){
global $errors;
global $j;
if (PingTry1($ip)==1){
//do nothing
}else{
$j++;
$errors[$j] = "$name --> $ip";
}
}
/*-----------------------------------------------------------------------------------------*/
//READ IN THE INI FILE INTO $filedata Array
$myFile1="hosts.ini";
$filehandle1 = fopen($myFile1, 'r') or die("Couldn't open file [$myFile1]");
$number1=count(file($myFile1));;
$filedata = fread($filehandle1, filesize($myFile1));
fclose($filehandle1);
// Create an array with each line of the file
$array1 = explode("rn", $filedata);
unset($filedata); //free up a bit of memory
foreach ($array1 as &$line) { // step through the array, line by line
if (!empty($line)){
list ($name,$ip)=split(",",$line);
DoTheCheck($name,$ip);
}
}
if ($errors){
echo 'The Following Hosts are down - <br/><br/><table>';
foreach ($errors as &$value) {
$k++;
echo '<tr><td><img class="light" src="red.png" /></td><td>'.$errors[$k].'</td></tr>';
}
echo '</tr></table>';
}
else{echo '<img class="light" src="green.png" /><h1>ALL IPS ARE UP!</h1>';}
?>
</body>
</html>
上面的代码适用于服务器,但它似乎根本不适用于思科交换机——可能与它的"ping"方式有关。
由于大学承诺等原因,我已经很久没有在这个脚本上做过任何工作了,但我已经尽可能多地在谷歌上做研究了,但无可否认,我充其量是2或3级PHP n00b。今天,我发现了一些适用于交换机的解决方案,但它们有5或6秒的超时时间,这是不可接受的,因为我希望系统尽可能多地循环,尽可能干净,并记录停机时间,以便以后绘图
例如-我尝试过这个:
function ping($host, $timeout = 1) {
/* ICMP ping packet with a pre-calculated checksum */
$package = "x08x00x7dx4bx00x00x00x00PingHost";
$socket = socket_create(AF_INET, SOCK_RAW, 1);
socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array('sec' => $timeout, 'usec' => 0));
socket_connect($socket, $host, null);
$ts = microtime(true);
socket_send($socket, $package, strLen($package), 0);
if (socket_read($socket, 255))
$result = microtime(true) - $ts;
else $result = false;
socket_close($socket);
return $result;
}
还有这个:
$url = '192.168.1.1';
$socket = ( bool )false;
$error = ( bool )false;
$socket = @fsockopen( $url, 23, $errno, $errstr, 1 ) or $error = ( bool )true;
if ( ( $socket ) && ( !$error ) )
{
echo "bound";
/* socket is bound - do something */
}
else
{
echo "not bound , [$errstr]";
/* socket is dead - errors are in $errno & $errstr */
}
if ($socket)fclose($socket);
当主机联机时,它们似乎都能工作,但如果我给它一个不存在的IP(用于测试,就好像主机脱机一样),在一个IP上需要大约5秒或更多的时间,这对我的需求来说太慢了。
是否可以使用pcntl_fork,甚至使用多线程卷曲来实现这一点?或者多次"exec"调用,甚至AJAX(我愿意在这个阶段尝试任何东西)
或者某种数据层(第2层)Mac扫描代码也会很棒——我不希望有人写完整的代码,但我相信以前做过这种事情的人会很清楚这些陷阱以及如何绕过它们。
总之,一个简单易行的解决方案会很好(我会继续梦想:-D),但任何帮助或建议都非常感谢。
编辑-在一些建议尝试在PEAR中的Net_Ping之后,我得到了以下代码:
<?php
$time1=date('H:i:s');
echo "Last Refresh Time = $time1<br/><hr/>";
//not sure if still needed - error_reporting(0);
require_once "Net/Ping.php";
$ping = Net_Ping::factory();
$ping->setArgs(array('count' => 2, 'ttl' => 50, 'timeout' => 1));
/*---------------------------------------------------------------------*/
function DoPing($ip)
{
global $ping;
$results = $ping->ping($ip);
if ($results->_loss==0) {return true;}else{return false;}
}
/*---------------------------------------------------------------------------------*/
function DoTheCheck($name,$ip){
global $errors;
global $j;
if (DoPing($ip)==1){
//do nothing
}else{
$j++;
$errors[$j] = "$name --> $ip";
}
}
/*-----------------------------------------------------------------------------------*/
//READ IN THE INI FILE INTO $filedata Array
$myFile1="hosts.ini";
$filehandle1 = fopen($myFile1, 'r') or die("Couldn't open file [$myFile1]");
$number1=count(file($myFile1));;
$filedata = fread($filehandle1, filesize($myFile1));
fclose($filehandle1);
// Create an array with each line of the file
$array1 = explode("rn", $filedata);
unset($filedata); //free up a bit of memory
foreach ($array1 as &$line) { // step through the array, line by line
if ( (!empty($line)) && (!strstr($line,'##')) ) {
list ($name,$ip)=split(",",$line);
DoTheCheck($name,$ip);
}
}
if ($errors){
echo 'The Following Hosts are down - <br/><br/><table>';
foreach ($errors as &$value) {
$k++;
echo '<tr><td><img class="light" src="red.png" /></td><td>'.$errors[$k].'</td></tr>';
}
echo '</tr></table>';
}
else{echo '<img class="light" src="green.png" /><h1>ALL IPS ARE UP!</h1>';}
?>
但那太慢了。。。检查大约20台服务器和10台交换机大约需要一两分钟。我需要添加大约100个开关,所以速度只会变慢。必须有更好的方法来做到这一点。再次强调,我们总是非常感谢任何帮助。我可能会尝试Munin,但实际上我需要一些可以集成到公司内部网(PHP)中的东西。
你试过像Munin这样的监控系统吗?它是已知的工作,而不是你的手工脚本。我用它来监控我的互联网连接,以及服务器是否可用;它为此提供了一个ping插件。穆尼恩也会在出现错误时发送邮件,并绘制漂亮的图形。
还有Nagios和Cacti,但我发现Munin是最容易设置的。
如果你真的真的想继续单独使用PHP,看看PEAR的Net_Ping包,它提供了一个发送Ping的API。
编辑:速度
由于一个接一个地ping所有主机太慢,因此需要并行化ping。Net_Ping不支持这一点,所以您必须并行运行几个PHP进程。使用PHP的pcntl函数或shell_exec
函数之一。ping脚本只需将主机作为ping的唯一命令行参数,并将ping结果记录在共享日志文件中。主脚本等待所有ping脚本结束并收集记录的信息。