PHP并行多任务研究(笔记)

最近遇到一个php爬虫的问题,在传统中,执行爬虫认为,php的file_get_contens每次只能爬取一个网页。因此爬虫运行中出现这样问题,前一秒执行爬取操作,网络下行满载,cpu负荷越0,下一秒钟cpu负荷满载,网络下行为0,这样就没有充分利用好网络和本地硬件资源,如果两者可以同时执行,那么原来2秒的操作优化后只要1秒来执行,效率提高100%。理论上cpui和网络同时满载是可行,下面思考下方案了。

1、爬取任务分摊

如果将100个网页爬取任务分为5份,然后分为t1.php、t2.php、t3.php、t4.php、t5.php这5个文件来处理,每个处理20个爬取任务,虽然整个过程不能提高400%的效率,但基本能最大限度利用网络资源和本地硬件资源。这种方式可以的,只是不够方便,需要人工开5个tab来执行。下面是依照上思路来的

2、php5的内置函数、模拟同时执行的多个请求

这里需要php5的两个函数stream_socket_client和stream_select,具体的可以查询手册,这里只说在此处的用法。

http://www.ibm.com/developerworks/cn/opensource/os-php-multitask/ ,这篇文章在介绍实现中比较详细,但是很可惜,这篇文章应该是大家从国外翻译过来的传抄者甚多,目前网上很多的相关代码基本上都不可用,这里的代码也有问题。经过研究和修改,这个给出可正常运行的代码。

<?php
date_default_timezone_set('PRC');

echo "Program starts at ". date('h:i:s') . ".\n<br>";
$timeout=10; 
$result=array(); 
$sockets=array(); 
$convenient_read_block=8192;
        
$delay=15;
$id=0;
while($delay>0)
{
	$s=stream_socket_client("phaseit.net:80", $errno,$errstr,$timeout);
	if($s)
	{
		$http_message="GET /demonstration/delay?delay=".$delay." HTTP/1.0\r\nHost: phaseit.net\r\nAccept: */*\r\n\r\n";
		fwrite($s,$http_message);
		$sockets[$id++]=$s;
	}
	else
	{
		echo "Stream " . $id . " failed to open correctly.";
	}
	$delay-= 3;
	//echo $delay;
} 
$w=NULL;
$e=NULL;

while (count($sockets))
{ 
	$read=$sockets;
	stream_select($read,$w,$e,$timeout);
	if (count($read)) 
	{
		foreach ($read as $r)
		{
			$id=array_search($r, $sockets); 
			$data=fread($r,$convenient_read_block); 
			if (strlen($data) == 0)
			{
				echo "Stream " . $id . " closes at " . date('h:i:s') . ".\n<br>";
				//echo $result[$id]."<br>";
				fclose($r); 
				unset($sockets[$id]); 
			}
			else
			{
				$result[$id].= $data; 
			}
		}
	}
	else
	{
		echo "Time-out!\n";
		break;
	}
}

?>
      

可正常执行。

ps:过程中出现的问题:Strict Standards: Only variables should be passed by reference(原因:源代码中stream_select($read, $w=null, $e=null, $timeout);  这种写法标准,某些环境会出错。相关wiki:http://efreedom.com/Question/1-9601698/PHP-Ignores-Passing-Reference-Var-Assigned-Function-Call 。


stream_socket_client的作用是打开5个远程端口(这里是打开t1.php、t2.php等5个本地端口),替代fsockopen,兼容性较好。

stream_select来分配执行,特别之处是能模拟5个请求并发,fgets是不断读取这个5个网页返回的结果,直到返回=0,表示远程的脚本执行结束(5个php请求都如此操作),然后谁先返回结果,谁就显示出Stream X closes at XXXX 来。

fwrite可以对流操作,具体逻辑也一样,既在流后面加上一段字符。

fgets读取流中字符。当然fgets涉及指针操作。

没理解可继续参考:http://blog.sina.com.cn/s/blog_60b9ee7f0100qdmh.html http://num7.iteye.com/blog/706613  http://www.alixixi.com/program/a/2011041269527.shtml

总结:这个脚本可能不是特别能显示出效果,但是该方法在多个爬虫任务运行时候,就能显示出特点了,并行爬取100个页面,能极大提高效率,摆脱php单线程的短板。

浏览量(274) | 此条目发表在lamp专区, 计算机分类目录,贴了, 标签。将固定链接加入收藏夹。