我必须废弃一个网站,我需要获取多个网址,然后逐个处理它们。目前的过程有点像这样。
我从此页面获取基本URL并获取所有辅助URL,然后为每个辅助URL获取该URL,处理找到的页面,下载一些照片(这需要相当长的时间)并将此数据存储到数据库中,然后获取下一个URL并重复该过程。
在这个过程中,我认为我在每次迭代开始时浪费一些时间来获取辅助URL。所以我在处理第一次迭代时尝试并行获取下一个URL。
我想到的解决方案是,从主进程调用一个PHP脚本,比如下载器,它将下载所有URL(带有curl_multi
或wget
)并将它们存储在某个数据库中。
我的问题是
答案 0 :(得分:6)
当我听到有人使用curl_multi_exec时,通常会发现他们只是用100个网址加载它,然后在完成后等待,然后全部处理它们,然后重新开始接下来的100个网址...怪我我也是这样做的,但后来我发现有可能在某些事情仍在进行时删除/添加curl_multi句柄,这确实可以节省大量时间,特别是如果你重用已经打开的连接。我写了一个小型库来处理带回调的请求队列;我当然不会在这里发布完整版(“小”仍然是相当多的代码),但这里有一个简化版本,主要给你一般的想法:
public function launch() {
$channels = $freeChannels = array_fill(0, $this->maxConnections, NULL);
$activeJobs = array();
$running = 0;
do {
// pick jobs for free channels:
while ( !(empty($freeChannels) || empty($this->jobQueue)) ) {
// take free channel, (re)init curl handle and let
// queued object set options
$chId = key($freeChannels);
if (empty($channels[$chId])) {
$channels[$chId] = curl_init();
}
$job = array_pop($this->jobQueue);
$job->init($channels[$chId]);
curl_multi_add_handle($this->master, $channels[$chId]);
$activeJobs[$chId] = $job;
unset($freeChannels[$chId]);
}
$pending = count($activeJobs);
// launch them:
if ($pending > 0) {
while(($mrc = curl_multi_exec($this->master, $running)) == CURLM_CALL_MULTI_PERFORM);
// poke it while it wants
curl_multi_select($this->master);
// wait for some activity, don't eat CPU
while ($running < $pending && ($info = curl_multi_info_read($this->master))) {
// some connection(s) finished, locate that job and run response handler:
$pending--;
$chId = array_search($info['handle'], $channels);
$content = curl_multi_getcontent($channels[$chId]);
curl_multi_remove_handle($this->master, $channels[$chId]);
$freeChannels[$chId] = NULL;
// free up this channel
if ( !array_key_exists($chId, $activeJobs) ) {
// impossible, but...
continue;
}
$activeJobs[$chId]->onComplete($content);
unset($activeJobs[$chId]);
}
}
} while ( ($running > 0 && $mrc == CURLM_OK) || !empty($this->jobQueue) );
}
在我的版本中,$ jobs实际上是单独的类,而不是控制器或模型的实例。他们只处理设置cURL选项,解析响应并调用给定的回调onComplete。 使用此结构,只要池中的某些内容完成,新请求就会立即启动。
当然,如果不只是检索需要时间而且处理也不会真正拯救你......而且它并不是真正的并行处理。但我仍然希望它有所帮助。 :)
P.S。为我做了一招。 :)一旦8小时工作现在使用50个连接池完成3-4分钟。无法形容那种感觉。 :)我真的不希望它按计划工作,因为使用PHP它很少完全按照假设...这就像“好吧,希望它至少在一个小时内完成......哇......等等..已经?!8-O“
答案 1 :(得分:2)
您可以使用curl_multi:http://www.somacon.com/p537.php
你可能还想考虑做这个客户端并使用Javascript。
另一个解决方案是编写一个你提交了一组URL的猎人/收集者,然后它完成并行工作并在完成后返回一个JSON数组。
换句话说:如果你有100个网址,你可以将该数组(也可能是JSON)发布到mysite.tld / huntergatherer - 它可以用你想要的任何语言做任何想做的事情,只返回JSON。
答案 2 :(得分:2)
除了curl multi解决方案之外,另一个只有一批gearman workers。如果你走这条路,我发现supervisord
是一个很好的方式来开始一大批deamon工人。
答案 3 :(得分:1)
除了CURL multi之外你还应该注意的事情:
虽然node.js,ruby EventMachine或类似工具非常适合做这些事情,但我提到的东西在PHP中也相当容易。
答案 4 :(得分:0)
尝试从PHP,python-pycurl脚本执行。比PHP卷曲更容易,更快。