curl_multi网页假死/卡死的解决方法

2014年06月23日 09:19 by:卡卡网

导读: curl_multi 多线程批量下载非常方便。不过curl_multi有时候并发下载的数量过多就会出现CPU过高,网页假死/卡死现象。可以使用curl_multi_select()函数来解决这个问题

curl_multi 多线程批量下载非常方便。不过curl_multi有时候并发下载的数量过多就会出现CPU过高,网页假死/卡死现象。

通过查询资料测试终于找到了一个解决问题的方法。

通常我们是这样使用curl_multi的:

  1. $connomains = array(
  2. "http://www.lao8.org",
  3. "http://www.163.com/",
  4. "http://www.sina.com.cn/"
  5. );
  6. $mh = curl_multi_init();
  7. foreach ($connomains as $i => $url) {
  8.      $conn[$i]=curl_init($url);
  9.       curl_setopt($conn[$i],CURLOPT_RETURNTRANSFER,1);
  10.       curl_multi_add_handle ($mh,$conn[$i]);
  11. }
  12. do { $n=curl_multi_exec($mh,$active); } while ($active);
  13. foreach ($connomains as $i => $url) {
  14.       $res[$i]=curl_multi_getcontent($conn[$i]);
  15.       curl_close($conn[$i]);
  16. }
  17. print_r($res);

这个实例代码有个致命弱点,就是在do循环的那段,在整个url请求期间是个死循环,它会轻易导致CPU占用很高,网页出现卡死状态。

经过测试可以使用curl_multi_select()函数来解决这个问题:

方法如下:

  1. do { $n=curl_multi_exec($mh,$active); } while ($active);

改为

  1. do {
  2.      $mrc = curl_multi_exec($mh,$active);
  3. } while ($mrc == CURLM_CALL_MULTI_PERFORM);
  4. while ($active and $mrc == CURLM_OK) {
  5.         if (curl_multi_select($mh) != -1) {
  6.            do {
  7.                     $mrc = curl_multi_exec($mh, $active);
  8.            } while ($mrc == CURLM_CALL_MULTI_PERFORM);
  9.         }
  10. }

因为$active要等全部url数据接受完毕才变成false,所以这里用到了curl_multi_exec的返回值判断是否还有数据,当有数据的时候就不停调用curl_multi_exec,暂时没有数据就进入select阶段,新数据一来就可以被唤醒继续执行。这里的好处就是CPU的无谓消耗没有了。

另外可能遇到的问题:

控制每一个请求的超时时间,在curl_multi_add_handle之前通过curl_setopt去做:

  1. curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);

判断是否超时了或者其他错误,在curl_multi_getcontent之前用:

  1. curl_error($conn[$i]);

了解multi接口

当程序需要进行多次curl并发请求的时候,curl提供的multi接口就派上用场了。流畅大致是这样的:

1)、curl_multi _init初始化一个multi curl对象,为了同时进行多个curl的并发访问,我们需要初始化多个easy curl对象,使用curl_easy_setopt进行相关设置。

2)、调用curl_multi _add_handle把easy curl对象添加到multi curl对象中。

3)、添加完毕后执行curl_multi_perform方法进行并发的访问。

4)、访问结束后curl_multi_remove_handle移除相关easy curl对象,curl_easy_cleanup清除easy curl对象。

5)、最后curl_multi_cleanup清除multi curl对象。

一个简单明了的PHP使用curl_multi_add_handle并行处理实例

  1. <?php
  2. // 创建一对cURL资源
  3. $ch1 = curl_init();
  4. $ch2 = curl_init();

  5. // 设置URL和相应的选项
  6. curl_setopt($ch1, CURLOPT_URL, "http://www.lao8.org/");
  7. curl_setopt($ch1, CURLOPT_HEADER, 0);
  8. curl_setopt($ch2, CURLOPT_URL, "http://www.baidu.com/");
  9. curl_setopt($ch2, CURLOPT_HEADER, 0);

  10. // 创建批处理cURL句柄
  11. $mh = curl_multi_init();

  12. // 增加2个句柄
  13. curl_multi_add_handle($mh,$ch1);
  14. curl_multi_add_handle($mh,$ch2);
  15. $running=null;

  16. // 执行批处理句柄
  17. do {
  18.     curl_multi_exec($mh,$running);
  19. } while($running > 0);

  20. // 关闭全部句柄
  21. curl_multi_remove_handle($mh, $ch1);
  22. curl_multi_remove_handle($mh, $ch2);
  23. curl_multi_close($mh);
  24. ?>