php cURL和Rolling cURL并发方式比较


Posted in PHP onOctober 30, 2013

在实际项目或者自己编写小工具(比如新闻聚合,商品价格监控,比价)的过程中, 通常需要从第3方网站或者API接口获取数据, 在需要处理1个URL队列时, 为了提高性能, 可以采用cURL提供的curl_multi_*族函数实现简单的并发。
本文将探讨两种具体的实现方法, 并对不同的方法做简单的性能对比.
1. 经典cURL并发机制及其存在的问题
经典的cURL实现机制在网上很容易找到, 比如参考PHP在线手册的如下实现方式:

function
classic_curl($urls,
$delay)
 {
    $queue
= curl_multi_init();
    $map
= array();
 
    foreach
($urls
as 
$url)
 {
        //
 create cURL resources
        $ch
= curl_init();
 
        //
 set URL and other appropriate options
        curl_setopt($ch,
 CURLOPT_URL, $url);
 
        curl_setopt($ch,
 CURLOPT_TIMEOUT, 1);
        curl_setopt($ch,
 CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch,
 CURLOPT_HEADER, 0);
        curl_setopt($ch,
 CURLOPT_NOSIGNAL, true);
 
        //
 add handle
        curl_multi_add_handle($queue,
$ch);
        $map[$url]
 = $ch;
    }
 
    $active
= null;
 
    //
 execute the handles
    do
{
        $mrc
= curl_multi_exec($queue,
$active);
    }
while
($mrc
== CURLM_CALL_MULTI_PERFORM);
 
    while
($active
> 0 && $mrc
== CURLM_OK) {
        if
(curl_multi_select($queue,
 0.5) != -1) {
            do
{
                $mrc
= curl_multi_exec($queue,
$active);
            }
while
($mrc
== CURLM_CALL_MULTI_PERFORM);
        }
    }
 
    $responses
= array();
    foreach
($map
as 
$url=>$ch)
 {
        $responses[$url]
 = callback(curl_multi_getcontent($ch),
$delay);
        curl_multi_remove_handle($queue,
$ch);
        curl_close($ch);
    }
 
    curl_multi_close($queue);
    return
$responses;
}

首先将所有的URL压入并发队列, 然后执行并发过程, 等待所有请求接收完之后进行数据的解析等后续处理. 在实际的处理过程中, 受网络传输的影响, 部分URL的内容会优先于其他URL返回, 但是经典cURL并发必须等待最慢的那个URL返回之后才开始处理, 等待也就意味着CPU的空闲和浪费. 如果URL队列很短, 这种空闲和浪费还处在可接受的范围, 但如果队列很长, 这种等待和浪费将变得不可接受.
2. 改进的Rolling cURL并发方式
仔细分析不难发现经典cURL并发还存在优化的空间, 优化的方式时当某个URL请求完毕之后尽可能快的去处理它, 边处理边等待其他的URL返回, 而不是等待那个最慢的接口返回之后才开始处理等工作, 从而避免CPU的空闲和浪费. 闲话不多说, 下面贴上具体的实现:
function
rolling_curl($urls,
$delay)
 {
    $queue
= curl_multi_init();
    $map
= array();
 
    foreach
($urls
as 
$url)
 {
        $ch
= curl_init();
 
        curl_setopt($ch,
 CURLOPT_URL, $url);
        curl_setopt($ch,
 CURLOPT_TIMEOUT, 1);
        curl_setopt($ch,
 CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch,
 CURLOPT_HEADER, 0);
        curl_setopt($ch,
 CURLOPT_NOSIGNAL, true);
 
        curl_multi_add_handle($queue,
$ch);
        $map[(string)
$ch]
 = $url;
    }
 
    $responses
= array();
    do
{
        while
(($code
= curl_multi_exec($queue,
$active))
 == CURLM_CALL_MULTI_PERFORM) ;
 
        if
($code
!= CURLM_OK) { break;
 }
 
        //
 a request was just completed -- find out which one
        while
($done
= curl_multi_info_read($queue))
 {
 
            //
 get the info and content returned on the request
            $info
= curl_getinfo($done['handle']);
            $error
= curl_error($done['handle']);
            $results
= callback(curl_multi_getcontent($done['handle']),
$delay);
            $responses[$map[(string)
$done['handle']]]
 = compact('info',
'error',
'results');
 
            //
 remove the curl handle that just completed
            curl_multi_remove_handle($queue,
$done['handle']);
            curl_close($done['handle']);
        }
 
        //
 Block for data in / output; error handling is done by curl_multi_exec
        if
($active
> 0) {
            curl_multi_select($queue,
 0.5);
        }
 
    }
while
($active);
 
    curl_multi_close($queue);
    return
$responses;
}

3. 两种并发实现的性能对比
改进前后的性能对比试验在LINUX主机上进行, 测试时使用的并发队列如下:

http://a.com/item.htm?id=14392877692
http:/a.com/item.htm?id=16231676302
http://a.com/item.htm?id=5522416710
http://a.com/item.htm?id=16551116403
简要说明下实验设计的原则和性能测试结果的格式: 为保证结果的可靠, 每组实验重复20次, 在单次实验中, 给定相同的接口URL集合, 分别测量Classic(指经典的并发机制)和Rolling(指改进后的并发机制)两种并发机制的耗时(秒为单位), 耗时短者胜出(Winner), 并计算节省的时间(Excellence, 秒为单位)以及性能提升比例(Excel. %). 为了尽量贴近真实的请求而又保持实验的简单, 在对返回结果的处理上只是做了简单的正则表达式匹配, 而没有进行其他复杂的操作. 另外, 为了确定结果处理回调对性能对比测试结果的影响, 可以使用usleep模拟现实中比较负责的数据处理逻辑(如提取, 分词, 写入文件或数据库等).
性能测试中用到的回调函数为:

function
callback($data,
$delay)
 {
    preg_match_all('/<h3>(.+)<\/h3>/iU',
$data,
$matches);
    usleep($delay);
    return
compact('data',
'matches');
}

数据处理回调无延迟时: Rolling Curl略优, 但性能提升效果不明显。
php cURL和Rolling cURL并发方式比较
PHP 相关文章推荐
php 时间计算问题小结
Jan 04 PHP
php教程之魔术方法的使用示例(php魔术函数)
Feb 12 PHP
一个经典的PHP验证码类分享
Nov 18 PHP
thinkphp特殊标签用法概述
Nov 24 PHP
你应该知道PHP浮点数知识
May 13 PHP
PHP中如何使用session实现保存用户登录信息
Oct 20 PHP
浅析Yii2集成富文本编辑器redactor实例教程
Apr 25 PHP
Yii2框架制作RESTful风格的API快速入门教程
Nov 08 PHP
创建无限极分类树型结构的简单方法
Jun 20 PHP
PHP实现redis限制单ip、单用户的访问次数功能示例
Jun 16 PHP
PHP随机数函数rand()与mt_rand()的讲解
Mar 25 PHP
关于PHP求解三数之和问题详析
Nov 09 PHP
使用PHP Socket写的POP3类
Oct 30 #PHP
腾讯QQ微博API接口获取微博内容
Oct 30 #PHP
FireFox浏览器使用Javascript上传大文件
Oct 30 #PHP
php使用ICQ网关发送手机短信
Oct 30 #PHP
PHP分页详细讲解(有实例)
Oct 30 #PHP
php预定义变量使用帮助(带实例)
Oct 30 #PHP
调整PHP的性能
Oct 30 #PHP
You might like
那些年一起学习的PHP(三)
2012/03/22 PHP
PHP中的traits实现代码复用使用实例
2015/05/13 PHP
javascript之更有效率的字符串替换
2008/08/02 Javascript
C#中TrimStart,TrimEnd,Trim在javascript上的实现
2011/01/17 Javascript
把jquery 的dialog和ztree结合实现步骤
2013/08/02 Javascript
javascript基本类型详解
2014/11/28 Javascript
零基础搭建Node.js、Express、Ejs、Mongodb服务器及应用开发入门
2014/12/20 Javascript
JS实现仿google、百度搜索框输入信息智能提示的实现方法
2015/04/20 Javascript
JavaScript实现动态删除列表框值的方法
2015/08/12 Javascript
jquery封装插件时匿名函数形参和实参的写法解释
2017/02/14 Javascript
JavaScript对象引用与赋值实例详解
2017/03/15 Javascript
详解Javascript获取缓存和清除缓存API
2017/05/25 Javascript
vue中$refs的用法及作用详解
2018/04/24 Javascript
JavaScript实现简单音乐播放器
2020/04/17 Javascript
JS使用new操作符创建对象的方法分析
2019/05/30 Javascript
Vue+Node实现的商城用户管理功能示例
2019/12/23 Javascript
[06:36]吞吞映像top1
2014/06/20 DOTA
[47:10]完美世界DOTA2联赛PWL S3 LBZS vs Rebirth 第二场 12.16
2020/12/18 DOTA
Python标准库之循环器(itertools)介绍
2014/11/25 Python
基于python中的TCP及UDP(详解)
2017/11/06 Python
Python实现基本数据结构中队列的操作方法示例
2017/12/04 Python
python requests post多层字典的方法
2018/12/27 Python
python自动化之Ansible的安装教程
2019/06/13 Python
解决pytorch GPU 计算过程中出现内存耗尽的问题
2019/08/19 Python
Python开发之pip安装及使用方法详解
2020/02/21 Python
Python callable内置函数原理解析
2020/03/05 Python
新手学习Python2和Python3中print不同的用法
2020/06/09 Python
pytorch随机采样操作SubsetRandomSampler()
2020/07/07 Python
python 如何用urllib与服务端交互(发送和接收数据)
2021/03/04 Python
Missguided美国官网:英国时尚品牌
2018/01/18 全球购物
FOREO斐珞尔官方旗舰店:LUNA露娜洁面仪
2018/03/11 全球购物
Exoticca英国:以最优惠的价格提供豪华异国情调旅行
2018/10/18 全球购物
向国旗敬礼活动小结
2014/09/27 职场文书
信访维稳工作汇报
2014/10/27 职场文书
祝福语集锦:给百岁老人祝寿贺词
2019/11/19 职场文书
python 爬取豆瓣网页的示例
2021/04/13 Python