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 相关文章推荐
怎样在UNIX系统下安装MySQL
Oct 09 PHP
PHP 错误之引号中使用变量
May 04 PHP
检查url链接是否已经有参数的php代码 添加 ? 或 &amp;
Feb 09 PHP
php中突破基于HTTP_REFERER的防盗链措施(stream_context_create)
Mar 29 PHP
PHP imagegrabscreen和imagegrabwindow(截取网站缩略图)的实例代码
Nov 07 PHP
ThinkPHP模板范围判断输出In标签与Range标签用法详解
Jun 30 PHP
php绘制一条弧线的方法
Jan 24 PHP
PHP 实现的将图片转换为TXT
Oct 21 PHP
thinkPHP分组后模板无法加载问题解决方法
Jul 12 PHP
利用PHP自动生成印有用户信息的名片
Aug 01 PHP
mysql查找删除重复数据并只保留一条实例详解
Sep 24 PHP
PHP和MYSQL实现分页导航思路详解
Apr 11 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
php模板函数 正则实现代码
2012/10/15 PHP
PHP连接MSSQL2008/2005数据库(SQLSRV)配置实例
2014/10/22 PHP
mac系统下为 php 添加 pcntl 扩展
2016/08/28 PHP
phpstudy隐藏index.php的方法
2020/09/21 PHP
获取URL地址中的文件名和参数的javascript代码
2009/09/02 Javascript
jQuery 自动增长的文本输入框实现代码
2010/04/02 Javascript
jQuery实现带动画效果的多级下拉菜单代码
2015/09/08 Javascript
JS获取数组最大值、最小值及长度的方法
2015/11/24 Javascript
JavaScript通过filereader接口读取文件
2017/05/10 Javascript
vue实现键盘输入支付密码功能
2018/08/18 Javascript
Webpack4 使用Babel处理ES6语法的方法示例
2019/03/07 Javascript
js前端如何写一个精确的倒计时代码
2019/10/25 Javascript
有趣的JavaScript隐式类型转换操作实例分析
2020/05/02 Javascript
three.js着色器材质的内置变量示例详解
2020/08/16 Javascript
openlayers实现图标拖动获取坐标
2020/09/25 Javascript
Vue项目如何引入bootstrap、elementUI、echarts
2020/11/26 Vue.js
[11:27]《一刀刀一天》之DOTA全时刻20:TI4总奖金突破920W TS赛事分析
2014/06/18 DOTA
[01:14:35]DOTA2上海特级锦标赛B组资格赛#1 Alliance VS Fnatic第一局
2016/02/26 DOTA
[00:37]DOTA2上海特级锦标赛 Secert 战队宣传片
2016/03/03 DOTA
Python兔子毒药问题实例分析
2015/03/05 Python
用python写一个windows下的定时关机脚本(推荐)
2017/03/21 Python
详谈python http长连接客户端
2017/06/12 Python
python中matplotlib实现最小二乘法拟合的过程详解
2017/07/11 Python
Python学习小技巧总结
2018/06/10 Python
对Python3.x版本print函数左右对齐详解
2018/12/22 Python
详解如何用python实现一个简单下载器的服务端和客户端
2019/10/28 Python
pytorch模型存储的2种实现方法
2020/02/14 Python
css3实现可拖动的魔方3d效果
2019/05/07 HTML / CSS
HTML5 Canvas——用路径描画线条实例介绍
2013/06/09 HTML / CSS
Parts Express:音频、视频和扬声器的第一来源
2017/04/25 全球购物
介绍一下sql server的安全性
2014/08/10 面试题
接口可以包含哪些成员
2012/09/30 面试题
Linux常见面试题
2016/10/04 面试题
2014年大学生党课心得体会范文
2014/03/29 职场文书
刑事辩护授权委托书
2014/09/13 职场文书
2014领导班子“四风问题”对照检查材料思想汇报(执法局)
2014/09/21 职场文书