PHP Curl多线程原理实例详解


Posted in PHP onNovember 06, 2013

给各位介绍一下Curl多线程实例与原理。不对之处请指教
相信许多人对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
这里有PHP手册上的例子:

<?php 
// 创建一对cURL资源 
$ch1 = curl_init(); 
$ch2 = curl_init(); // 设置URL和相应的选项 
curl_setopt($ch1, CURLOPT_URL, "https://3water.com/"); 
curl_setopt($ch1, CURLOPT_HEADER, 0); 
curl_setopt($ch2, CURLOPT_URL, "http://www.php.net/"); 
curl_setopt($ch2, CURLOPT_HEADER, 0); 
// 创建批处理cURL句柄 
$mh = curl_multi_init(); 
// 增加2个句柄 
curl_multi_add_handle($mh,$ch1); 
curl_multi_add_handle($mh,$ch2); 
$active = null; 
// 执行批处理句柄 
do { 
    $mrc = curl_multi_exec($mh, $active); 
} while ($mrc == CURLM_CALL_MULTI_PERFORM); 
while ($active && $mrc == CURLM_OK) { 
    if (curl_multi_select($mh) != -1) { 
        do { 
            $mrc = curl_multi_exec($mh, $active); 
        } while ($mrc == CURLM_CALL_MULTI_PERFORM); 
    } 
} 
// 关闭全部句柄 
curl_multi_remove_handle($mh, $ch1); 
curl_multi_remove_handle($mh, $ch2); 
curl_multi_close($mh); 
?>

整个使用过程差不多就是这样,但是,这个简单代码有个致命弱点,就是在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]);

本类的特点:
运行非常稳定。
设置一个并发就会始终以这个并发数进行工作,即使通过回调函数添加任务也不影响。
CPU占用极低,绝大部分CPU消耗在用户的回调函数上。
内存利用率高,任务数量较多(15W个任务占用内存会超过256M)可以使用回调函数添加任务,个数自定。
能够最大限度的占用带宽。
链式任务,比如一个任务需要从多个不同的地址采集数据,可以通过回调一气呵成。
能够对CURL错误进行多次尝试,次数自定(大并发一开始容易产生CURL错误,网络状况或对方服务器稳定性也有可能产生CURL错误)。
回调函数相当灵活,可以多种类型任务同时进行(比如下载文件,抓取网页,分析404可以在一个PHP进程中同时进行)。
可以非常容易的定制任务类型,比如检查404,获取redirect的最后url等。
可以设置缓存,挑战产品节操。
不足:
不能充分利用多核CPU(可以开多个进程解决,需要自己处理任务分割等逻辑)。
最大并发500(或512?),经过测试是CURL 内部限制,超过最大并发会导致总是返回失败。
目前没有断点续传功能。
目前任务是原子性的,不能对一个大文件分为几部分分别开线程下载。

PHP 相关文章推荐
比较时间段一与时间段二是否有交集的php函数
May 31 PHP
PHP转换IP地址到真实地址的方法详解
Jun 09 PHP
解析link_mysql的php版
Jun 30 PHP
php文件夹与文件目录操作函数介绍
Sep 09 PHP
php发送post请求的三种方法
Feb 11 PHP
php实现的mongodb操作类
May 28 PHP
PHP 输出缓冲控制(Output Control)详解
Aug 25 PHP
解决laravel 5.1报错:No supported encrypter found的办法
Jun 07 PHP
创建无限极分类树型结构的简单方法
Jun 20 PHP
Laravel学习教程之IOC容器的介绍与用例
Aug 15 PHP
浅析PHP7的多进程及实例源码
Apr 14 PHP
tp5 实现列表数据根据状态排序
Oct 18 PHP
php二维数组用键名分组相加实例函数
Nov 06 #PHP
php不用正则验证真假身份证
Nov 06 #PHP
php 中文字符串首字母的获取函数分享
Nov 04 #PHP
PHP图片上传代码
Nov 04 #PHP
PHP CURL获取cookies模拟登录的方法
Nov 04 #PHP
php Session存储到Redis的方法
Nov 04 #PHP
在PHP中使用redis
Nov 04 #PHP
You might like
ThinkPHP中ajax使用实例教程
2014/08/22 PHP
Yii2实现上下联动下拉框功能的方法
2016/08/10 PHP
php集成开发环境详解
2019/09/24 PHP
thinkphp5.1 框架导入/导出excel文件操作示例
2020/05/25 PHP
PHP实现页面静态化深入讲解
2021/03/04 PHP
JavaScript入门教程(10) 认识其他对象
2009/01/31 Javascript
jQuery的实现原理的模拟代码 -5 Ajax
2010/08/07 Javascript
理解javascript中的回调函数(callback)
2014/09/02 Javascript
Node.js 制作实时多人游戏框架
2015/01/08 Javascript
javascript随机显示背景图片的方法
2015/06/18 Javascript
跟我学习javascript的浮点数精度
2015/11/16 Javascript
如何实现星星评价(jquery.raty.js插件)
2016/12/21 Javascript
Vuejs入门教程之Vue生命周期,数据,手动挂载,指令,过滤器
2017/04/19 Javascript
第一次记录Bootstrap table学习笔记(1)
2017/05/18 Javascript
jquery操作ul的一些操作笔记整理(干货)
2017/08/31 jQuery
node微信开发之获取access_token+自定义菜单
2019/03/17 Javascript
微信小程序获取地理位置及经纬度授权代码实例
2019/09/18 Javascript
python 二分查找和快速排序实例详解
2017/10/13 Python
Python实现一个简单的验证码程序
2017/11/03 Python
python opencv之分水岭算法示例
2018/02/24 Python
详解Python爬取并下载《电影天堂》3千多部电影
2019/04/26 Python
PyQt4 treewidget 选择改变颜色,并设置可编辑的方法
2019/06/17 Python
keras Lambda自定义层实现数据的切片方式,Lambda传参数
2020/06/11 Python
python实现从ftp上下载文件的实例方法
2020/07/19 Python
python 服务器运行代码报错ModuleNotFoundError的解决办法
2020/09/16 Python
协程Python 中实现多任务耗资源最小的方式
2020/10/19 Python
美国护肤咨询及美容产品电商:Askderm
2017/02/24 全球购物
全球最大的生存食品、水和装备专用在线市场:BePrepared.com
2020/01/02 全球购物
高考自主招生自荐信
2013/10/20 职场文书
预备党员思想汇报范文
2013/12/29 职场文书
《乡下孩子》教学反思
2014/04/17 职场文书
仓管员岗位职责
2015/02/03 职场文书
挂职锻炼个人总结
2015/03/05 职场文书
疾病证明书
2015/06/19 职场文书
导游词之云南丽江-泸沽湖
2019/09/26 职场文书
Spring Cache和EhCache实现缓存管理方式
2021/06/15 Java/Android