解析php中curl_multi的应用


Posted in PHP onJuly 17, 2013

相信许多人对php手册中语焉不详的curl_multi一族的函数头疼不已,它们文档少,给的例子 更是简单的让你无从借鉴,我也曾经找了许多网页,都没见一个完整的应用例子。
•curl_multi_add_handle
•curl_multi_close
•curl_multi_exec
•curl_multi_getcontent
•curl_multi_info_read
•curl_multi_init
•curl_multi_remove_handle
•curl_multi_select
一般来说,想到要用这些函数时,目的显然应该是要同时请求多个url,而不是一个一个依次请求,否则不如自己循环去调curl_exec好了。

步骤总结如下:
第一步:调用curl_multi_init
第二步:循环调用curl_multi_add_handle
这一步需要注意的是,curl_multi_add_handle的第二个参数是由curl_init而来的子handle。
第三步:持续调用curl_multi_exec
第四步:根据需要循环调用curl_multi_getcontent获取结果
第五步:调用curl_multi_remove_handle,并为每个字handle调用curl_close
第六步:调用curl_multi_close

这里有一个网上找的简单例子,其作者称为dirty的例子,(稍后我会说明为何dirty):

*
Here's a quick and dirty example for curl-multi from PHP, tested on PHP 5.0.0RC1 CLI / FreeBSD 5.2.1
*/
$connomains = array(
"http://www.cnn.com/",
"http://www.canada.com/",
"http://www.yahoo.com/"
);
$mh = curl_multi_init();
foreach ($connomains as $i => $url) {
     $conn[$i]=curl_init($url);
      curl_setopt($conn[$i],CURLOPT_RETURNTRANSFER,1);
      curl_multi_add_handle ($mh,$conn[$i]);
}
do { $n=curl_multi_exec($mh,$active); } while ($active);
foreach ($connomains as $i => $url) {
      $res[$i]=curl_multi_getcontent($conn[$i]);
      curl_close($conn[$i]);
}
print_r($res);

整个使用过程差不多就是这样,但是,这个简单代码有个致命弱点,就是在do循环的那段,在整个url请求期间是个死循环,它会轻易导致CPU占用100%。

现在我们来改进它,这里要用到一个几乎没有任何文档的函数curl_multi_select了,虽然C的curl库对select有说明,但是,php里的接口和用法确与C中有不同。

把上面do的那段改成下面这样:

do {
                        $mrc = curl_multi_exec($mh,$active);
                } while ($mrc == CURLM_CALL_MULTI_PERFORM);
                while ($active and $mrc == CURLM_OK) {
                        if (curl_multi_select($mh) != -1) {
                                do {
                                        $mrc = curl_multi_exec($mh, $active);
                                } while ($mrc == CURLM_CALL_MULTI_PERFORM);
                        }
                }

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

另外:还有一些细节的地方可能有时候要遇到:
控制每一个请求的超时时间,在curl_multi_add_handle之前通过curl_setopt去做:
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);

判断是否超时了或者其他错误,在curl_multi_getcontent之前用:curl_error($conn[$i]);

注意:php的multi_curl功能慎用,因为某些版本的curl和php的搭配有Bug。所以你调试过没问题的代码很可能在别的机器上不正确。

例如今天我发现在php5.2.2搭配curl/7.16.2版本如果CURLOPT_USERAGENT属性设置成某些值,则实际发送的HTTP头的会变成一串二进制值。

而恰好这个版本的php中strip_tags函数对含二进制的数据处理的不好,这才发现了这个Bug

PHP 相关文章推荐
php 网页播放器用来播放在线视频的代码(自动判断并选择视频文件类型)
Jun 03 PHP
基于PHP的cURL快速入门教程 (小偷采集程序)
Jun 02 PHP
php中如何防止表单的重复提交
Aug 02 PHP
ThinkPHP令牌验证实例
Jun 18 PHP
ThinkPHP中处理表单中的注意事项
Nov 22 PHP
php去掉文件前几行的方法
Jul 29 PHP
PHP获取某个月最大天数(最后一天)的方法
Jul 29 PHP
非集成环境的php运行环境(Apache配置、Mysql)搭建安装图文教程
Apr 12 PHP
php 截取GBK文档某个位置开始的n个字符方法
Mar 08 PHP
ThinkPHP框架分布式数据库连接方法详解
Mar 14 PHP
PHP十六进制颜色随机生成器功能示例
Jul 24 PHP
PHP+Session防止表单重复提交的解决方法
Apr 09 PHP
php curl获取网页内容(IPV6下超时)的解决办法
Jul 16 #PHP
ie与session丢失(新窗口cookie丢失)实测及解决方案
Jul 15 #PHP
实测在class的function中include的文件中非php的global全局环境
Jul 15 #PHP
Php output buffering缓存及程序缓存深入解析
Jul 15 #PHP
PHP 转义使用详解
Jul 15 #PHP
thinkphp 一个页面使用2次分页的实现方法
Jul 15 #PHP
shell脚本作为保证PHP脚本不挂掉的守护进程实例分享
Jul 15 #PHP
You might like
最贵的咖啡是怎么产生的,它的风味怎么样?
2021/03/04 新手入门
php 缓存函数代码
2008/08/27 PHP
smarty内置函数foreach用法实例
2015/01/22 PHP
PHP使用PDO操作sqlite数据库应用案例
2019/03/07 PHP
PHP中echo与print区别点整理
2021/03/09 PHP
JavaScript中实现块作用域的方法
2010/04/01 Javascript
javascript 原型继承介绍
2011/08/30 Javascript
THREE.JS入门教程(6)创建自己的全景图实现步骤
2013/01/25 Javascript
JS.getTextContent(element,preformatted)使用介绍
2013/09/21 Javascript
js实现checkbox全选和反选示例
2014/05/01 Javascript
JS去除重复并统计数量的实现方法
2016/12/15 Javascript
Vue的Flux框架之Vuex状态管理器
2017/07/30 Javascript
js实现HTML中Select二级联动的实例
2018/01/05 Javascript
jQuery实现的五星点评功能【案例】
2019/02/18 jQuery
js中数组常用方法总结(推荐)
2019/04/09 Javascript
[05:36]DOTA2 2015国际邀请赛中国区预选赛第四日TOP10
2015/05/29 DOTA
[01:05:29]DOTA2-DPC中国联赛 正赛 PSG.LGD vs Aster BO3 第二场 1月24日
2021/03/11 DOTA
python练习程序批量修改文件名
2014/01/16 Python
django框架之cookie/session的使用示例(小结)
2018/10/15 Python
pandas 转换成行列表进行读取与Nan处理的方法
2018/10/30 Python
python简易实现任意位数的水仙花实例
2018/11/13 Python
python 通过SSHTunnelForwarder隧道连接redis的方法
2019/02/19 Python
python中bytes和str类型的区别
2019/10/21 Python
opencv 图像轮廓的实现示例
2020/07/08 Python
Python 实现集合Set的示例
2020/12/21 Python
诗普兰迪官方网站:Splendid
2018/09/18 全球购物
求职简历的自我评价
2014/01/31 职场文书
电子商务系毕业生自荐信
2014/05/29 职场文书
省级优秀毕业生主要事迹
2014/05/29 职场文书
学习朴航瑛老师爱岗敬业先进事迹思想汇报
2014/09/17 职场文书
运动会广播稿150字(9篇)
2014/09/20 职场文书
爱情保证书
2015/01/17 职场文书
2015年档案室工作总结
2015/05/23 职场文书
Python并发编程实例教程之线程的玩法
2021/06/20 Python
一文搞懂Redis中String数据类型
2022/04/03 Redis
js作用域及作用域链工作引擎
2022/07/07 Javascript