用PHP多进程校验WEB站点能否打开(新增python模块)
时间:2007-01-09 来源:blau
数据库中有个一个1万条以上的网站列表,并且没有都会更新,变大。现在需要用php去校验这些网站能否打开,把能打开的筛选出来。一般都是要用fsockopen函数连接到目录网站,然后发送一个HTTP请求
$out = "GET / HTTP/1.0\r\n";
$out .= "Host: www.example.com\r\n";
$out .= "Connection: Close\r\n\r\n";
如果在返回的HTTP头中找到200说明网站能访问,否则有问题。建立socket是很耗费时间的,能连上还好说,如果连不上等待的时间相当长,如果是单进程的话会跑死人的,因此考虑用exec或popen建立多个进程来跑。
为了方便理解,程序分两部分,一个是调度程序main.php,一个是执行检查的子程序sub.php先看sub.php,接收传入的参数,经过一系列古怪的筛选后,开始fsockopen连接校验
sub.php
?php
//获取传入的域名
if(checker($_SERVER['argv'][1],$_SERVER['argv'][2]))
{
echo 1;
}
else
{
echo 0;
}
function checker($domain,$path)
{
//默认dns服务器的域名
$cnet=array("216.239.113.148");
//对纯ip地址不进行检查,默认为能访问
$array=explode(".",$domain);
if(is_numeric($array[0]) && is_numeric($array[1]) && is_numeric($array[2]))
{
return true;
}
//含有端口号的不检测,默认能访问
if(stristr($domain,":"))
{
return true;
}
//返回的ip是万网的dns
$ip=gethostbyname($domain);
$array=explode(".",$ip);
//ip是万网的dns服务器,或返回的不是ip地址
if(in_array($ip,$cnet) || !is_numeric($array[0]))
{
return false;
}
//连接到目标域名
$timeout = 3;
$fp
[email==@fsockopen($domain,80,$errno,$errmsg,$timeout]= @fsockopen($domain,80,$errno,$errmsg,$timeout[/email]
) ;
stream_set_timeout($fp, $timeout);
if (!$fp)
{
return false;
}
else
{
$request="GET ".$path." HTTP/1.0\r\n\r\n";
$request .= "Host:".$domain."\r\n";
$request .= "Connection: Close\r\n\r\n";
@fwrite($fp, $request);
$info = stream_get_meta_data($fp);
if ($info['timed_out'])
{
return false;
}
else
{
$replyString = fgets($fp, 256);
preg_match('|^HTTP/\S+ (\d+) |i', $replyString, $a );
if($a[1]==200)
{
return true;
}
}
unset($replyString);
fclose($fp);
}
return false;
}
?>
下面是main.php的函数,为了降低和数据库的链接次数,所浪费的时间,先将数据库中的域名和子目录取出来保存在数组中,这样可能会造成main进程使用的内存超过8M限制的可能。pconnect没试过,对于需要执行几个小时的php程序不是很放心 T_T
main.php
?php
set_time_limit(0);
include("mysql_db.php");
$licsql = new YQ_SQL;
//查询所有的数据
$sql = "SELECT domain,path FROM domain_cache";
$licsql->query($sql);
$time_start=time();
$i=0;
while($licsql->next_record())
{
//结果结合
$table[$i]['domain']=$licsql->f("domain");
$table[$i]['path']=$licsql->f("path")?$licsql->f("path"):"/";
//计数器前移
$i++;
}
$licsql->close();
//开启的进程数
$exec_number = 20;
//任务总数
$tasklist_len=$i;
//任务计数器
$tasklist_pos = 0;
//结果输出
$handle_list = array();
$i=0;
while(1)
{
//子进程列表有空闲,则填充补齐子进程列表
//如果允许的进程数大于当前的进程数 && 任务的位置小于任何列表的长度,则进入开启新进程的流程
if($exec_number > count($handle_list) && $tasklist_pos $tasklist_len)
{
//$i等于未执行任务的开始,$i要小于任务的长度
for($i=$tasklist_pos;$i$tasklist_len; )
{
//构造要执行的命令
$command="/usr/local/bin/php sub.php ".$table[$i]['domain']." ".$table[$i]['path'];
//开启管道,执行命令,执行命令的结果保存$handle_list数组中,开启完管道就闪人,不会等管道命令执行完毕(这是关键)
$handle_list[]=popen($command , "r" );
echo "\n";
echo(date("Y-m-d H:i:s",time())." task ".$command);
//开启下一个popen管道
$i++;
//当允许的进程数等于已经开启的进程数时直接跳出开启新进程的流程
if($exec_number == count($handle_list)) break;
}
//任务计数器往前移动$i位
$tasklist_pos=$i;
}
//如果子进程列表空,退出
if(0 == count($handle_list))
{
break;
}
//检查子进程列表的输出,把停掉的子进程关闭并记录下来
$end_handle_keys = array();
foreach($handle_list as $key => $handle)
{
//将结果保存到table数组中,$i=$key
$table[$key][]=fread($handle,16);
//如果已经执行完毕
if(feof($handle))
{
//记录失败
if($table[$key][0]=='0')
{
setStatus($table[$key]['domain'],"fail");
}
//记录成功
if($table[$key][0]=='1')
{
setStatus($table[$key]['domain'],"good");
}
//记录执行完毕的进程
$end_handle_keys[] = $key;
//关闭管道
pclose($handle);
}
}
//踢出停掉的子进程
foreach($end_handle_keys as $key)
{
//进程列表减掉已经执行完毕的
unset($handle_list[$key]);
}
}
// print_r($table);
unset($end_handle_keys);
unset($handle_list);
unset($table);
$time_end=time();
echo "\n";
echo 'Time ESC '.($time_end-$time_start).'s';
//执行一次记录操作
function setStatus($domain,$status)
{
$licsql = new YQ_SQL;
if($status=="fail")
{
$sql="UPDATE domain_cache SET isalive='0' WHERE domain='".$domain."'";
}
if($status=="good")
{
$sql="UPDATE domain_cache SET isalive='1' WHERE domain='".$domain."'";
}
echo "\n";
echo $sql;
$licsql->query($sql);
$licsql->close();
}
}
?>
下面是一个Python编写的模块,性能要好很多
sub.py
#/usr/bin/python
#Test a website
from socket import socket,AF_INET,SOCK_STREAM
import sys
import re
host = sys.argv[1]
port = 80
path = sys.argv[2]
sock = socket(AF_INET,SOCK_STREAM)
sock.connect((host,port))
sock.send("GET "+path+" HTTP/1.0\r\n\r\n")
status = re.findall(r'HTTP/\S+ (\d+) ',sock.recv(16))
print status
if status[0] == '200':
print "1"
else:
print "0"
$out = "GET / HTTP/1.0\r\n";
$out .= "Host: www.example.com\r\n";
$out .= "Connection: Close\r\n\r\n";
如果在返回的HTTP头中找到200说明网站能访问,否则有问题。建立socket是很耗费时间的,能连上还好说,如果连不上等待的时间相当长,如果是单进程的话会跑死人的,因此考虑用exec或popen建立多个进程来跑。
为了方便理解,程序分两部分,一个是调度程序main.php,一个是执行检查的子程序sub.php先看sub.php,接收传入的参数,经过一系列古怪的筛选后,开始fsockopen连接校验
sub.php
?php
//获取传入的域名
if(checker($_SERVER['argv'][1],$_SERVER['argv'][2]))
{
echo 1;
}
else
{
echo 0;
}
function checker($domain,$path)
{
//默认dns服务器的域名
$cnet=array("216.239.113.148");
//对纯ip地址不进行检查,默认为能访问
$array=explode(".",$domain);
if(is_numeric($array[0]) && is_numeric($array[1]) && is_numeric($array[2]))
{
return true;
}
//含有端口号的不检测,默认能访问
if(stristr($domain,":"))
{
return true;
}
//返回的ip是万网的dns
$ip=gethostbyname($domain);
$array=explode(".",$ip);
//ip是万网的dns服务器,或返回的不是ip地址
if(in_array($ip,$cnet) || !is_numeric($array[0]))
{
return false;
}
//连接到目标域名
$timeout = 3;
$fp
[email==@fsockopen($domain,80,$errno,$errmsg,$timeout]= @fsockopen($domain,80,$errno,$errmsg,$timeout[/email]
) ;
stream_set_timeout($fp, $timeout);
if (!$fp)
{
return false;
}
else
{
$request="GET ".$path." HTTP/1.0\r\n\r\n";
$request .= "Host:".$domain."\r\n";
$request .= "Connection: Close\r\n\r\n";
@fwrite($fp, $request);
$info = stream_get_meta_data($fp);
if ($info['timed_out'])
{
return false;
}
else
{
$replyString = fgets($fp, 256);
preg_match('|^HTTP/\S+ (\d+) |i', $replyString, $a );
if($a[1]==200)
{
return true;
}
}
unset($replyString);
fclose($fp);
}
return false;
}
?>
下面是main.php的函数,为了降低和数据库的链接次数,所浪费的时间,先将数据库中的域名和子目录取出来保存在数组中,这样可能会造成main进程使用的内存超过8M限制的可能。pconnect没试过,对于需要执行几个小时的php程序不是很放心 T_T
main.php
?php
set_time_limit(0);
include("mysql_db.php");
$licsql = new YQ_SQL;
//查询所有的数据
$sql = "SELECT domain,path FROM domain_cache";
$licsql->query($sql);
$time_start=time();
$i=0;
while($licsql->next_record())
{
//结果结合
$table[$i]['domain']=$licsql->f("domain");
$table[$i]['path']=$licsql->f("path")?$licsql->f("path"):"/";
//计数器前移
$i++;
}
$licsql->close();
//开启的进程数
$exec_number = 20;
//任务总数
$tasklist_len=$i;
//任务计数器
$tasklist_pos = 0;
//结果输出
$handle_list = array();
$i=0;
while(1)
{
//子进程列表有空闲,则填充补齐子进程列表
//如果允许的进程数大于当前的进程数 && 任务的位置小于任何列表的长度,则进入开启新进程的流程
if($exec_number > count($handle_list) && $tasklist_pos $tasklist_len)
{
//$i等于未执行任务的开始,$i要小于任务的长度
for($i=$tasklist_pos;$i$tasklist_len; )
{
//构造要执行的命令
$command="/usr/local/bin/php sub.php ".$table[$i]['domain']." ".$table[$i]['path'];
//开启管道,执行命令,执行命令的结果保存$handle_list数组中,开启完管道就闪人,不会等管道命令执行完毕(这是关键)
$handle_list[]=popen($command , "r" );
echo "\n";
echo(date("Y-m-d H:i:s",time())." task ".$command);
//开启下一个popen管道
$i++;
//当允许的进程数等于已经开启的进程数时直接跳出开启新进程的流程
if($exec_number == count($handle_list)) break;
}
//任务计数器往前移动$i位
$tasklist_pos=$i;
}
//如果子进程列表空,退出
if(0 == count($handle_list))
{
break;
}
//检查子进程列表的输出,把停掉的子进程关闭并记录下来
$end_handle_keys = array();
foreach($handle_list as $key => $handle)
{
//将结果保存到table数组中,$i=$key
$table[$key][]=fread($handle,16);
//如果已经执行完毕
if(feof($handle))
{
//记录失败
if($table[$key][0]=='0')
{
setStatus($table[$key]['domain'],"fail");
}
//记录成功
if($table[$key][0]=='1')
{
setStatus($table[$key]['domain'],"good");
}
//记录执行完毕的进程
$end_handle_keys[] = $key;
//关闭管道
pclose($handle);
}
}
//踢出停掉的子进程
foreach($end_handle_keys as $key)
{
//进程列表减掉已经执行完毕的
unset($handle_list[$key]);
}
}
// print_r($table);
unset($end_handle_keys);
unset($handle_list);
unset($table);
$time_end=time();
echo "\n";
echo 'Time ESC '.($time_end-$time_start).'s';
//执行一次记录操作
function setStatus($domain,$status)
{
$licsql = new YQ_SQL;
if($status=="fail")
{
$sql="UPDATE domain_cache SET isalive='0' WHERE domain='".$domain."'";
}
if($status=="good")
{
$sql="UPDATE domain_cache SET isalive='1' WHERE domain='".$domain."'";
}
echo "\n";
echo $sql;
$licsql->query($sql);
$licsql->close();
}
}
?>
下面是一个Python编写的模块,性能要好很多
sub.py
#/usr/bin/python
#Test a website
from socket import socket,AF_INET,SOCK_STREAM
import sys
import re
host = sys.argv[1]
port = 80
path = sys.argv[2]
sock = socket(AF_INET,SOCK_STREAM)
sock.connect((host,port))
sock.send("GET "+path+" HTTP/1.0\r\n\r\n")
status = re.findall(r'HTTP/\S+ (\d+) ',sock.recv(16))
print status
if status[0] == '200':
print "1"
else:
print "0"
相关阅读 更多 +
排行榜 更多 +