CURL学习(二)

curl学习(一) 中 所做的工作都是针对一个url的单次请求,当 要处理1个URL队列时, 为了提高性能, 可以采用CURL提供的curl_multi_*族函数实现简单的并发.
$urls =  array(
    'http://www.cnblogs.com',
    'http://www.google.com.hk',
    'http://www.baidu.com',
    'http://www.weibo.com',
    'http://www.comsenz.com',
    'http://www.csdn.net',
);
$opt_arr =  array(
    CURLOPT_HEADER =>  FALSE,
    CURLOPT_TIMEOUT => 3,
    CURLOPT_RETURNTRANSFER =>  TRUE,
);
// 句柄初始化
$multi = curl_multi_init();
// 参数设置
foreach( $urls  as  $key =>  $url) {
     $ch_arr[ $key] = curl_init(); // 初始化一个curl资源
     $opt_arr[CURLOPT_URL] =  $url; // 设置url
    curl_setopt_array( $ch_arr[ $key],  $opt_arr); // 参数设置
    curl_multi_add_handle( $multi$ch_arr[ $key]); // 加入句柄队列
}

// 预定义一个状态变量
$isrunning =  NULL;
// 执行批处理句柄
do {
     $mrc = curl_multi_exec( $multi$isrunning); // $isrunning 一个用来判断操作是否仍在执行的标识的引用。
while( $mrc == CURLM_CALL_MULTI_PERFORM);  // 常量 CURLM_CALL_MULTI_PERFORM 代表还有一些刻不容缓的工作要做

while( $isrunning &&  $mrc == CURLM_OK) {
     if(curl_multi_select( $multi) != -1) { // curl_multi_select阻塞直到cURL批处理连接中有活动连接,失败时返回-1
         do {
             $mrc = curl_multi_exec( $multi$isrunning);
        }  while( $mrc == CURLM_CALL_MULTI_PERFORM);
    }
}
// 所有请求接收完之后进行数据的解析等后续处理
foreach( $ch_arr  as  $key =>  $ch) {
     // 获取内容进行后续处理
     $contents = curl_multi_getcontent( $ch);
     // do something to deal data
    curl_multi_remove_handle( $multi$ch_arr[ $key]); // 关闭句柄
    curl_close( $ch);

}
curl_multi_close($multi);

注意:CURL在PHP中的多线程处理其实并不是真正的多线程,而是用单线程批处理模拟的多线程效果 


上述代码显然有缺陷:数据处理都是在所有url请求接收完成以后才进行的,如果某些url处理比较慢显然就耽误了整个队列的处理时间,造成了CUP的闲置和浪费,这显然不是我们想要的结果。但是我们可以这样处理:每当一个url请求完成就开始处理,同时等待其它url请求返回。代码如下:

$urls =  array(
    'http://www.cnblogs.com',
    'http://www.google.com.hk',
    'http://www.baidu.com',
    'http://www.weibo.com',
    'http://www.comsenz.com',
    'http://www.csdn.net',
    'http://www.php.net',
);
$opt_arr =  array(
    CURLOPT_HEADER =>  FALSE,
    CURLOPT_TIMEOUT => 5,
    CURLOPT_RETURNTRANSFER =>  TRUE,
);
// 句柄初始化
$multi = curl_multi_init();
// 参数设置
foreach( $urls  as  $key =>  $url) {
     $ch_arr[ $key] = curl_init(); // 初始化一个curl资源
     $opt_arr[CURLOPT_URL] =  $url; // 设置url
    curl_setopt_array( $ch_arr[ $key],  $opt_arr); // 参数设置
    curl_multi_add_handle( $multi$ch_arr[ $key]); // 加入句柄队列
}
// 预定义一个状态变量
$isrunning =  NULL;
// 执行批处理句柄
do {
     while(( $mrc = curl_multi_exec( $multi$isrunning)) == CURLM_CALL_MULTI_PERFORM); // $isrunning 一个用来判断操作是否仍在执行的标识的引用。
     if( $mrc != CURLM_OK)
         break;
     while( $done = curl_multi_info_read( $multi)) { // 成功时返回相关信息的数组,失败时返回FALSE
         $key =  array_search( $done['handle'],  $ch_arr);
         if(curl_getinfo( $done['handle'], CURLINFO_HTTP_CODE) == '200') {
             $content = curl_multi_getcontent( $done['handle']);
             // deal $content
             echo ++ $j,":  $urls[ $key] : ", strlen( $content),"\n";
            curl_multi_remove_handle( $multi$done['handle']);
            curl_close( $done['handle']);
        }  else {
             echo ++ $j,": ", $urls[ $key],": ",curl_error( $done['handle']),"\n";
        }
    }
while( $isrunning);
curl_multi_close( $multi);

上述代码仍然是有缺陷的,不知道聪明的读者您发现没有?当URL队列很大时(比如1000),这就是一个大并发显然不合理的,参考这里

function rolling_curl( $urls$callback$custom_options =  null) {

     $rolling_window = 5;
     $rolling_window = ( sizeof( $urls) <  $rolling_window) ?  sizeof( $urls) :  $rolling_window;

     $master = curl_multi_init();
     $curl_arr =  array();

     // 设置curl参数
     $std_options =  array(CURLOPT_RETURNTRANSFER =>  true,
    CURLOPT_FOLLOWLOCATION =>  true,
    CURLOPT_MAXREDIRS => 5);
     $options = ( $custom_options) ? ( $std_options +  $custom_options) :  $std_options;

     // 初始化curl资源队列
     $arr_chs =  array();
     for ( $i = 0;  $i <  $rolling_window$i++) {
         $arr_chs[ $urls[ $i]] = curl_init();
         $options[CURLOPT_URL] =  $urls[ $i];
        curl_setopt_array( $arr_chs[ $urls[ $i]], $options);
        curl_multi_add_handle( $master$arr_chs[ $urls[ $i]]);
    }

     do {
         while(( $execrun = curl_multi_exec( $master$running)) == CURLM_CALL_MULTI_PERFORM);
         if( $execrun != CURLM_OK) {
             break;
        }
         while( $done = curl_multi_info_read( $master)) {
             $info = curl_getinfo( $done['handle']);
             if ( $info['http_code'] == 200)  {
            
                 $content = curl_multi_getcontent( $done['handle']);
                 $callback( $content);
                
                 // 新建一个curl资源并加入并发队列
                 if( $i <  sizeof( $urls)) {
                     $arr_chs[ $urls[ $i]] = curl_init();
                     $options[CURLOPT_URL] =  $urls[ $i];   //  increment i
                    curl_setopt_array( $arr_chs[ $urls[ $i]],  $options);
                    curl_multi_add_handle( $master$arr_chs[ $urls[ $i]]);
                }
                
                curl_multi_remove_handle( $master$done['handle']);
                curl_close( $done['handle']);
            }  else {
            
                echo curl_errno( $done['handle']),":",curl_error( $done['handle']),"\n";
            }
        } 
    }  while( $running);

    curl_multi_close( $master);
     return  true;
}

转载于:https://www.cnblogs.com/caoyuhan/archive/2012/09/18/2690829.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值