php function:真正的并发请求,curl_multi,PHP多线程并发

jackxiang 2010-10-22 13:05 | |
c版的curl_multi为php扩展准备

#include <stdio.h>
#include <string.h>
/* somewhat unix-specific */
#include <sys/time.h>
#include <unistd.h>
/* curl stuff */
#include <curl/curl.h>
/*
* Simply download two HTTP files!
*/
int main(int argc, char **argv)
   {  
     CURL *http_handle;
     CURL *http_handle2;
     CURLM *multi_handle;
     int still_running; /* keep number of running handles */
     http_handle = curl_easy_init();
     http_handle2 = curl_easy_init();
    /* set options */
     curl_easy_setopt(http_handle, CURLOPT_URL, "http://www.sohu.com/");
    /* set options */
    curl_easy_setopt(http_handle2, CURLOPT_URL, "http://www.sina.com.cn/");
     /* init a multi stack */
     multi_handle = curl_multi_init();
    /* add the individual transfers */
     curl_multi_add_handle(multi_handle, http_handle);
     curl_multi_add_handle(multi_handle, http_handle2);
     /* we start some action by calling perform right away */
    while(CURLM_CALL_MULTI_PERFORM == curl_multi_perform(multi_handle, &still_running));
           while(still_running) {
                struct timeval timeout;
                int rc; /* select() return code */
                fd_set fdread;
                fd_set fdwrite;
                fd_set fdexcep;
                int maxfd;
                 FD_ZERO(&fdread);
                FD_ZERO(&fdwrite);
                FD_ZERO(&fdexcep);
                /* set a suitable timeout to play around with */
                timeout.tv_sec = 1;
                timeout.tv_usec = 0;
                /* get file descriptors from the transfers */

              curl_multi_fdset(multi_handle, &fdread, &fdwrite, &fdexcep, &maxfd);
            /* In a real-world program you OF COURSE check the return code of the
              function calls, *and* you make sure that maxfd is bigger than -1 so
             that the call to select() below makes sense! */
                rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
                switch(rc) {
                            case -1:
                            /* select error */
                            break;
                            case 0:
                            default:
                           /* timeout or readable/writable sockets */
                            while(CURLM_CALL_MULTI_PERFORM ==curl_multi_perform(multi_handle, &still_running));
                            break;
                           }
           }
    curl_multi_cleanup(multi_handle);
    curl_easy_cleanup(http_handle);
    curl_easy_cleanup(http_handle2);
    return 0;
    }


编译的时候会报错


gcc curl_mult.cpp -o aa -lcurl


需要加上


gcc curl_mult.cpp -o aa -lcurl -lstdc++


来源:http://hi.baidu.com/lostdays/blog/item/e5f88a22270e61fad7cae2d0.html







这是一个自己写的函数

由于blog位置显示的原因,请点击code的原文

http://www.trackself.com/code/curl_multi.html

关键在于GOOGLE一下:$mh = curl_multi_init();   以curl_multi为关键词则可

程序的好处在于我将日常的需要都写在里面了,而且节约CPU和内存
  function cmi($connomains,$killspace=TRUE,$forhtml=TRUE,$timeout=6,$header=0,$follow=1){
   /*
    cmi该函数的目的在于并发请求多个url,然后返回http://www.trackself.com编写,真正的PHP并发
      原文发表在http://www.trackself.com/archives/463.html
    此并发请求在url多于2的时候,明显比for ... file_get_contents ...要优很多
    核心是curl库的curl_multi方法
    用法:
    $urls=array(
          'http://www.google.com',
          'http://www.baidu.com',
          'http://sina.com',
          'http://163.com'
          )
    $htmls=cmi($urls);
    print_r($htmls);
    
    //传入的$connomains是URL一维数组,由http://www.trackself.com编写
    //该函数的目的在于并发请求多个url,然后返回源码
    //以一次性略增加CPU为代价
    //来减轻服务器因为for ... file_get_contents ...的长时连接负担及内存和CPU的负担,所以并发数不要大多(50以内效果非常好),尽量不要用于单页面或3页面以内的请求  
    //$killspace为真时表示自动去掉HTML中换行及多余的空白,$forhtml为真时表示反回源码,为faluse时就是并发执行请求了(可以用于计划任务)
    //后面的几个参数的详细说明请看注释,毕竟函数不长
     */
    
        $res=array();
      $urlsa=array();
      $results=array();
        $mh = curl_multi_init();//创建多curl对象,为了几乎同时执行    
        foreach ($connomains as $i => $url) {
           $conn[$i]=curl_init($url);//若url中含有gb2312汉字,例如FTP时,要在传入url的时候处理一下,这里不用
           curl_setopt($conn[$i], CURLOPT_TIMEOUT, $timeout);//此时间须根据页面的HTML源码出来的时间,一般是在1s内的,慢的话应该也不会6秒,极慢则是在16秒内
          curl_setopt($conn[$i], CURLOPT_HEADER, $header);//不返回请求头,只要源码
            curl_setopt($conn[$i],CURLOPT_RETURNTRANSFER,1);//必须为1
             curl_setopt($conn[$i], CURLOPT_FOLLOWLOCATION, $follow);//如果页面含有自动跳转的代码如301或者302HTTP时,自动拿转向的页面
            curl_multi_add_handle ($mh,$conn[$i]);//关键,一定要放在上面几句之下,将单curl对象赋给多对象
        }
        //下面一大步的目的是为了减少cpu的无谓负担,暂时不明,来自php.net的建议,几乎是固定用法
        do {
            $mrc = curl_multi_exec($mh,$active);//当无数据时或请求暂停时,active=true
        } while ($mrc == CURLM_CALL_MULTI_PERFORM);//当正在接受数据时
        while ($active and $mrc == CURLM_OK) {//当无数据时或请求暂停时,active=true,为了减少cpu的无谓负担,这一步很难明啊
            if (curl_multi_select($mh) != -1) {
                do {
                    $mrc = curl_multi_exec($mh, $active);
                } while ($mrc == CURLM_CALL_MULTI_PERFORM);
            }
        }
        /////////////////////////////////////////////////////////////////////////////////////////
      //下面返回结果
        foreach ($connomains as $i => $url) {
            $cinfo=curl_getinfo($conn[$i]);//可用于取得一些有用的参数,可以认为是header
            $url=$cinfo[url];//真实url,有些url
          if($killspace){//有点水消耗
            $str=trim(curl_multi_getcontent($conn[$i]));
            $str = preg_replace('/\s(?=\s)/', '', $str);//去掉跟随别的挤在一块的空白        
                    $str = preg_replace('/[\n\r\t]/', ' ', $str);  //最后,去掉非space 的空白,用一个空格代替
                $res[$i]=stripslashes($str);//取得对象源码,并取消换行,节约内存的同时,可以方便作正则处理
          }else{
            $res[$i]=curl_multi_getcontent($conn[$i]);
          }
          if(!$forhtml){//节约内存      
            $res[$i]=NULL;
          }
         /*下面这一段放一些高消耗的程序代码,用来处理HTML,我保留的一句=NULL是要提醒,及时清空对象释放内存,此程序在并发过程中如果源码太大,内在消耗严重
         //事实上,这里应该做一个callback函数或者你应该将你的逻辑直接放到这里来,我为了程序可重复,没这么做
             preg_match_all($preg,$res[$i],$matchlinks);
             $res[$i]=NULL;
                 */  
                  curl_close($conn[$i]);//关闭所有对象
                  curl_multi_remove_handle($mh  , $conn[$i]);   //用完马上释放资源                    
          
        }
        curl_multi_close($mh);$mh=NULL;$conn=NULL;
        
        return $res;
    }//cmi


--------------------------------------------------------------------------------
下面是版本二,几乎一至的代码,但输出的形式改为为带key;
--------------------------------------------------------------------------------


function cmi($connomains,$killspace=TRUE,$forhtml=TRUE,$timeout=6,$header=0,$follow=1){
   /*
    cmi该函数的目的在于并发请求多个url,然后返回http://www.trackself.com编写,真正的PHP并发
      原文发表在http://www.trackself.com/archives/463.html
    此并发请求在url多于2的时候,明显比for ... file_get_contents ...要优很多
    核心是curl库的curl_multi方法
    用法:
    //array_flip(array_flip($connomains))
    $urls=array(
          'http://www.google.com',
          'http://www.baidu.com',
          'http://sina.com',
          'http://163.com'
          )
    $urls=array_flip(array_flip($connomains));//去除url中的重复的项,注意传入urls时一定要系合法的url表达,虽然不会影响其它url执行,但会减慢执行速度
    $htmls=cmi($urls);
    print_r($htmls);
    
    //传入的$connomains是URL一维数组,由http://www.trackself.com编写
    //该函数的目的在于并发请求多个url,然后返回源码
    //以一次性略增加CPU为代价
    //来减轻服务器因为for ... file_get_contents ...的长时连接负担及内存和CPU的负担,所以并发数不要大多(50以内效果非常好),尽量不要用于单页面或3页面以内的请求  
    //$killspace为真时表示自动去掉HTML中换行及多余的空白,$forhtml为真时表示反回源码,为faluse时就是并发执行请求了(可以用于计划任务)
    //后面的几个参数的详细说明请看注释,毕竟函数不长
     */
    
        $res=array();//用于保存结果        
    //$connomains=array_flip(array_flip($connomains));//去除url中的重复项
        $mh = curl_multi_init();//创建多curl对象,为了几乎同时执行    
        foreach ($connomains as $i => $url) {
           $conn[$url]=curl_init($url);//若url中含有gb2312汉字,例如FTP时,要在传入url的时候处理一下,这里不用
           curl_setopt($conn[$url], CURLOPT_TIMEOUT, $timeout);//此时间须根据页面的HTML源码出来的时间,一般是在1s内的,慢的话应该也不会6秒,极慢则是在16秒内
          curl_setopt($conn[$url], CURLOPT_HEADER, $header);//不返回请求头,只要源码
            curl_setopt($conn[$url],CURLOPT_RETURNTRANSFER,1);//必须为1
             curl_setopt($conn[$url], CURLOPT_FOLLOWLOCATION, $follow);//如果页面含有自动跳转的代码如301或者302HTTP时,自动拿转向的页面
            curl_multi_add_handle ($mh,$conn[$url]);//关键,一定要放在上面几句之下,将单curl对象赋给多对象
        }
        //下面一大步的目的是为了减少cpu的无谓负担,暂时不明,来自php.net的建议,几乎是固定用法
        do {
            $mrc = curl_multi_exec($mh,$active);//当无数据时或请求暂停时,active=true
        } while ($mrc == CURLM_CALL_MULTI_PERFORM);//当正在接受数据时
        while ($active and $mrc == CURLM_OK) {//当无数据时或请求暂停时,active=true,为了减少cpu的无谓负担,这一步很难明啊
            if (curl_multi_select($mh) != -1) {
                do {
                    $mrc = curl_multi_exec($mh, $active);
                } while ($mrc == CURLM_CALL_MULTI_PERFORM);
            }
        }
        /////////////////////////////////////////////////////////////////////////////////////////
      //下面返回结果
        foreach ($connomains as $i => $url) {
            $cinfo=curl_getinfo($conn[$url]);//可用于取得一些有用的参数,可以认为是header
            //$url=$cinfo[url];//真实url,有些url
          if($killspace){//有点水消耗
            $str=trim(curl_multi_getcontent($conn[$url]));
            $str = preg_replace('/\s(?=\s)/', '', $str);//去掉跟随别的挤在一块的空白        
                    $str = preg_replace('/[\n\r\t]/', ' ', $str);  //最后,去掉非space 的空白,用一个空格代替
                $res[$url]=stripslashes($str);//取得对象源码,并取消换行,节约内存的同时,可以方便作正则处理
          }else{
            $res[$url]=curl_multi_getcontent($conn[$url]);
          }
          if(!$forhtml){//节约内存      
            $res[$url]=NULL;
          }
         /*下面这一段放一些高消耗的程序代码,用来处理HTML,我保留的一句=NULL是要提醒,及时清空对象释放内存,此程序在并发过程中如果源码太大,内在消耗严重
         //事实上,这里应该做一个callback函数或者你应该将你的逻辑直接放到这里来,我为了程序可重复,没这么做
             preg_match_all($preg,$res[$i],$matchlinks);
             $res[$i]=NULL;
                 */  
                  curl_close($conn[$url]);//关闭所有对象
                  curl_multi_remove_handle($mh  , $conn[$url]);   //用完马上释放资源                    
          
        }
        curl_multi_close($mh);$mh=NULL;$conn=NULL;$connomains=NULL;
        
        return $res;
    }//cmi



作者:jackxiang@向东博客 专注WEB应用 构架之美 --- 构架之美,在于尽态极妍 | 应用之美,在于药到病除
地址:https://jackxiang.com/post/3670/
版权所有。转载时必须以链接形式注明作者和原始出处及本声明!


最后编辑: jackxiang 编辑于2010-10-22 13:05
评论列表
发表评论

昵称

网址

电邮

打开HTML 打开UBB 打开表情 隐藏 记住我 [登入] [注册]