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 array_push()数组函数:将一个或多个单元压入数组的末尾(入栈)
Jul 12 PHP
PHP表单提交表单名称含有点号(.)则会被转化为下划线(_)
Dec 14 PHP
使用php 获取时间今天明天昨天时间戳的详解
Jun 20 PHP
有关于PHP中常见数据类型的汇总分享
Jan 06 PHP
PHP获取指定函数定义在哪个文件中以及其所在的行号实例
May 08 PHP
ThinkPHP框架实现session跨域问题的解决方法
Jul 01 PHP
php静态文件返回304技巧分享
Jan 06 PHP
PHP实现微信JS-SDK接口选择相册及拍照并上传的方法
Dec 05 PHP
php格式文件打开的四种方法
Feb 24 PHP
PHP实现的mysql读写分离操作示例
May 22 PHP
PHP7.1实现的AES与RSA加密操作示例
Jun 15 PHP
详解php命令注入攻击
Apr 06 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
投票管理程序
2006/10/09 PHP
php 地区分类排序算法
2013/07/01 PHP
由php中字符offset特征造成的绕过漏洞详解
2017/07/07 PHP
对laravel的csrf 防御机制详解,及form中csrf_token()的存在介绍
2019/10/24 PHP
ppk谈JavaScript style属性
2008/10/10 Javascript
检测是否已安装 .NET Framework 3.5的js脚本
2009/02/14 Javascript
jQuery extend 的简单实例
2013/09/18 Javascript
用js控制组织结构图可以任意拖拽到指定位置
2014/01/17 Javascript
Extjs的FileUploadField文件上传出现了两个上传按钮
2014/04/29 Javascript
jQuery文件上传插件Uploadify使用指南
2014/06/05 Javascript
JavaScript基础函数整理汇总
2015/01/30 Javascript
简介EasyUI datagrid editor combogrid搜索框的实现
2016/04/01 Javascript
jQuery自定义图片缩放拖拽插件imageQ实现方法(附demo源码下载)
2016/05/27 Javascript
利用JS提交表单的几种方法和验证(必看篇)
2016/09/17 Javascript
angular+ionic 的app上拉加载更新数据实现方法
2017/01/16 Javascript
jQuery实现按比例缩放图片的方法
2017/04/29 jQuery
JS运动特效之完美运动框架实例分析
2018/01/24 Javascript
vue cli构建的项目中请求代理与项目打包问题
2018/02/26 Javascript
vue.js动画中的js钩子函数的实现
2018/07/06 Javascript
JQuery中的常用事件、对象属性与使用方法分析
2019/12/23 jQuery
[16:56]heroes英雄教学 司夜刺客
2014/09/18 DOTA
Python将多个excel文件合并为一个文件
2018/01/03 Python
Python+pandas计算数据相关系数的实例
2018/07/03 Python
python常用函数与用法示例
2019/07/02 Python
Python3列表List入门知识附实例
2020/02/09 Python
Django+python服务器部署与环境部署教程详解
2020/03/30 Python
Elasticsearch py客户端库安装及使用方法解析
2020/09/14 Python
python tqdm库的使用
2020/11/30 Python
css3实现超炫风车特效
2014/11/12 HTML / CSS
德国圣伯纳德草药屋:Kräuterhaus Sanct Bernhard(有中文站)
2018/08/05 全球购物
瑞典香水、须后水和美容产品购物网站:Parfym-Klick.se
2019/12/29 全球购物
工程力学硕士生的自我评价范文
2013/11/16 职场文书
七一表彰活动方案
2014/01/18 职场文书
团日活动策划书
2014/02/01 职场文书
婚纱摄影师求职信范文
2014/04/17 职场文书
党的群众路线教育实践活动批评与自我批评范文
2014/10/16 职场文书