开发者社区> 航空母舰> 正文

php curl批处理--可控并发异步

简介:
+关注继续查看

通常情况下 PHP 中的 cURL 是阻塞运行的,就是说创建一个 cURL 请求以后必须等它执行成功或者超时才会执行下一个请求:API接口访问一般会首选CURL

在实际项目或者自己编写小工具(比如新闻聚合,商品价格监控,比价)的过程中, 通常需要从第3方网站或者API接口获取数据, 在需要处理1个URL队列时, 为了提高性能, 可以采用cURL提供的curl_multi_*族函数实现简单的并发.

Java代码  收藏代码
  1. <?php  
  2. include 'curl.class.php';  
  3. function callback($response, $info, $error, $request)  
  4. {  
  5.     echo 'response:<br>';  
  6.     print_r($response);  
  7.   
  8.     echo '<br>' . date("Y-m-d H:i:s") . '&nbsp;&nbsp;&nbsp;<br>';  
  9.     echo '<br>' . str_repeat("-"100) . '<br>';  
  10. }  
  11.   
  12. $USER_COOKIE = (!empty($_REQUEST['cookie'])) ? $_REQUEST['cookie'] : file_get_contents("cookie.txt");  
  13.   
  14. $curl = new Curl ("callback");  
  15.   
  16. $data = array(  
  17.     array(  
  18.         'url' => 'http://dyactive2.vip.xunlei.com/com_sign/?game=qmr&type=rec_gametime&referfrom=&rt=0.42521539455332336', //秦美人  
  19.         'method' => 'POST',  
  20.         'post_data' => '',  
  21.         'header' => null,  
  22.         'options' => array(  
  23.             CURLOPT_REFERER => "http://niu.xunlei.com/entergame/?gameNo=qmr&fenQuNum=3",  
  24.             CURLOPT_COOKIE => $USER_COOKIE,  
  25.         )  
  26.     ),  
  27.     array(  
  28.         'url' => 'http://dyactive2.vip.xunlei.com/com_sign/?game=sq&type=rec_gametime&referfrom=&rt=0.42521539455332336', //神曲  
  29.         'method' => 'POST',  
  30.         'post_data' => '',  
  31.         'header' => null,  
  32.         'options' => array(  
  33.             CURLOPT_REFERER => "http://niu.xunlei.com/entergame/?gameNo=sq&fenQuNum=41",  
  34.             CURLOPT_COOKIE => $USER_COOKIE,  
  35.         )  
  36.     ),  
  37.     array(  
  38.         'url' => 'http://dyactive2.vip.xunlei.com/com_sign/?game=frxz&type=rec_gametime&referfrom=&rt=0.42521539455332336', //凡人修真  
  39.         'method' => 'POST',  
  40.         'post_data' => '',  
  41.         'header' => null,  
  42.         'options' => array(  
  43.             CURLOPT_REFERER => "http://niu.xunlei.com/entergame/?gameNo=frxz&fenQuNum=3",  
  44.             CURLOPT_COOKIE => $USER_COOKIE,  
  45.         )  
  46.     ),  
  47.     array(  
  48.         'url' => 'http://dyactive2.vip.xunlei.com/com_sign/?game=smxj&type=rec_gametime&referfrom=&rt=0.42521539455332336', //神魔仙界  
  49.         'method' => 'POST',  
  50.         'post_data' => '',  
  51.         'header' => null,  
  52.         'options' => array(  
  53.             CURLOPT_REFERER => "http://niu.xunlei.com/entergame/?gameNo=smxj&fenQuNum=2",  
  54.             CURLOPT_COOKIE => $USER_COOKIE,  
  55.         )  
  56.     ),  
  57.     array(  
  58.         'url' => 'http://dyactive2.vip.xunlei.com/com_sign/?game=qsqy&type=rec_gametime&referfrom=&rt=0.42521539455332336', //倾世情缘  
  59.         'method' => 'POST',  
  60.         'post_data' => '',  
  61.         'header' => null,  
  62.         'options' => array(  
  63.             CURLOPT_REFERER => "http://niu.xunlei.com/entergame/?gameNo=qsqy&fenQuNum=11",  
  64.             CURLOPT_COOKIE => $USER_COOKIE,  
  65.         )  
  66.     ),  
  67. );  
  68.   
  69. foreach ($data as $val) {  
  70.     $request = new Curl_request ($val ['url'], $val ['method'], $val ['post_data'], $val ['header'], $val ['options']);  
  71.     $curl->add($request);  
  72. }  
  73.   
  74. $curl->execute();  
  75. echo $curl->display_errors();  
使用下来效果很好,没有副作用,并发数可控,应用之处多多,自己发挥想象吧
Java代码  收藏代码
  1. <?php  
  2. /** 
  3.  * cURL批量处理  工具类 
  4.  *  
  5.  * @since Version 1.0 
  6.  * @author Justmepzy <justmepzy@gmail.com> 
  7.  * @link http://t.qq.com/JustPzy 
  8.  */  
  9.   
  10.   
  11. /** 
  12.  *单一的请求对象 
  13.  */  
  14. class Curl_request {  
  15.     public $url         = '';  
  16.     public $method      = 'GET';  
  17.     public $post_data   = null;  
  18.     public $headers     = null;  
  19.     public $options     = null;  
  20.     /** 
  21.      *  
  22.      * @param string $url 
  23.      * @param string $method 
  24.      * @param string $post_data 
  25.      * @param string $headers 
  26.      * @param array $options 
  27.      * @return void 
  28.      */  
  29.     public function __construct($url, $method = 'GET', $post_data = null, $headers = null, $options = null) {  
  30.         $this->url = $url;  
  31.         $this->method = strtoupper( $method );  
  32.         $this->post_data = $post_data;  
  33.         $this->headers = $headers;  
  34.         $this->options = $options;  
  35.     }  
  36.     /** 
  37.      * @return void 
  38.      */  
  39.     public function __destruct() {  
  40.         unset ( $this->url, $this->method, $this->post_data, $this->headers, $this->options );  
  41.     }  
  42. }  
  43. /** 
  44.  * 包含请求列队处理 
  45.  */  
  46. class Curl {  
  47.     /** 
  48.      * 请求url个数 
  49.      * @var int 
  50.      */  
  51.     private $size           = 5;  
  52.     /** 
  53.      * 等待所有cURL批处理中的活动连接等待响应时间 
  54.      * @var int 
  55.      */  
  56.     private $timeout        = 5;  
  57.     /** 
  58.      * 完成请求回调函数 
  59.      * @var string 
  60.      */  
  61.     private $callback       = null;  
  62.     /** 
  63.      * cRUL配置 
  64.      * @var array 
  65.      */  
  66.     private $options        = array (CURLOPT_SSL_VERIFYPEER => 0,CURLOPT_RETURNTRANSFER => 1,CURLOPT_CONNECTTIMEOUT => 30 );  
  67.     /** 
  68.      * 请求头 
  69.      * @var array 
  70.      */  
  71.     private $headers        = array ();  
  72.     /** 
  73.      * 请求列队 
  74.      * @var array 
  75.      */  
  76.     private $requests       = array ();  
  77.     /** 
  78.      * 请求列队索引 
  79.      * @var array 
  80.      */  
  81.     private $request_map    = array ();  
  82.     /** 
  83.      * 错误 
  84.      * @var array 
  85.      */  
  86.     private $errors         = array ();  
  87.     /** 
  88.      * @access public 
  89.      * @param string $callback 回调函数 
  90.      * 该函数有4个参数($response,$info,$error,$request) 
  91.      * $response    url返回的body 
  92.      * $info        cURL连接资源句柄的信息 
  93.      * $error       错误 
  94.      * $request     请求对象 
  95.      */  
  96.     public function __construct($callback = null) {  
  97.         $this->callback = $callback;  
  98.     }  
  99.     /** 
  100.      * 添加一个请求对象到列队 
  101.      * @access public 
  102.      * @param object $request 
  103.      * @return boolean 
  104.      */  
  105.     public function add($request) {  
  106.         $this->requests [] = $request;  
  107.         return TRUE;  
  108.     }  
  109.     /** 
  110.      * 创建一个请求对象并添加到列队 
  111.      * @access public 
  112.      * @param string $url 
  113.      * @param string $method 
  114.      * @param string $post_data 
  115.      * @param string $headers 
  116.      * @param array $options 
  117.      * @return boolean 
  118.      */  
  119.     public function request($url, $method = 'GET', $post_data = null, $headers = null, $options = null) {  
  120.         $this->requests [] = new Curl_request ( $url, $method, $post_data, $headers, $options );  
  121.         return TRUE;  
  122.     }  
  123.     /** 
  124.      * 创建GET请求对象 
  125.      * @access public 
  126.      * @param string $url 
  127.      * @param string $headers 
  128.      * @param array $options 
  129.      * @return boolean 
  130.      */  
  131.     public function get($url, $headers = null, $options = null) {  
  132.         return $this->request ( $url, "GET"null, $headers, $options );  
  133.     }  
  134.     /** 
  135.      * 创建一个POST请求对象 
  136.      * @access public 
  137.      * @param string $url 
  138.      * @param string $post_data 
  139.      * @param string $headers 
  140.      * @param array $options 
  141.      * @return boolean 
  142.      */  
  143.     public function post($url, $post_data = null, $headers = null, $options = null) {  
  144.         return $this->request ( $url, "POST", $post_data, $headers, $options );  
  145.     }  
  146.     /** 
  147.      * 执行cURL 
  148.      * @access public 
  149.      * @param int $size 最大连接数 
  150.      * @return Ambigous <boolean, mixed>|boolean 
  151.      */  
  152.     public function execute($size = null) {  
  153.         if (sizeof ( $this->requests ) == 1) {  
  154.             return $this->single_curl ();  
  155.         } else {  
  156.             return $this->rolling_curl ( $size );  
  157.         }  
  158.     }  
  159.     /** 
  160.      * 单个url请求 
  161.      * @access private 
  162.      * @return mixed|boolean 
  163.      */  
  164.     private function single_curl() {  
  165.         $ch = curl_init ();  
  166.         $request = array_shift ( $this->requests );  
  167.         $options = $this->get_options ( $request );  
  168.         curl_setopt_array ( $ch, $options );  
  169.         $output = curl_exec ( $ch );  
  170.         $info = curl_getinfo ( $ch );  
  171.           
  172.         // it's not neccesary to set a callback for one-off requests  
  173.         if ($this->callback) {  
  174.             $callback = $this->callback;  
  175.             if (is_callable ( $this->callback )) {  
  176.                 call_user_func ( $callback, $output, $info, $request );  
  177.             }  
  178.         } else  
  179.             return $output;  
  180.         return true;  
  181.     }  
  182.     /** 
  183.      * 多个url请求 
  184.      * @access private 
  185.      * @param int $size 最大连接数 
  186.      * @return boolean 
  187.      */  
  188.     private function rolling_curl($size = null) {  
  189.         if ($size)  
  190.             $this->size = $size;  
  191.         else   
  192.             $this->size = count($this->requests);  
  193.         if (sizeof ( $this->requests ) < $this->size)  
  194.             $this->size = sizeof ( $this->requests );  
  195.         if ($this->size < 2)  
  196.             $this->set_error ( 'size must be greater than 1' );  
  197.         $master = curl_multi_init ();  
  198.         //添加cURL连接资源句柄到map索引  
  199.         for($i = 0; $i < $this->size; $i ++) {  
  200.             $ch = curl_init ();  
  201.             $options = $this->get_options ( $this->requests [$i] );  
  202.             curl_setopt_array ( $ch, $options );  
  203.             curl_multi_add_handle ( $master, $ch );  
  204.               
  205.             $key = ( string ) $ch;  
  206.             $this->request_map [$key] = $i;  
  207.         }  
  208.           
  209.         $active = $done = null;  
  210.         do {  
  211.             while ( ($execrun = curl_multi_exec ( $master, $active )) == CURLM_CALL_MULTI_PERFORM )  
  212.                 ;  
  213.             if ($execrun != CURLM_OK)  
  214.                 break;  
  215.             //有一个请求完成则回调  
  216.             while ( $done = curl_multi_info_read ( $master ) ) {  
  217.                 //$done 完成的请求句柄  
  218.                 $info = curl_getinfo ( $done ['handle'] );//  
  219.                 $output = curl_multi_getcontent ( $done ['handle'] );//  
  220.                 $error = curl_error ( $done ['handle'] );//  
  221.                   
  222.                 $this->set_error ( $error );  
  223.                   
  224.                 //调用回调函数,如果存在的话  
  225.                 $callback = $this->callback;  
  226.                 if (is_callable ( $callback )) {  
  227.                     $key = ( string ) $done ['handle'];  
  228.                     $request = $this->requests [$this->request_map [$key]];  
  229.                     unset ( $this->request_map [$key] );  
  230.                     call_user_func ( $callback, $output, $info, $error, $request );  
  231.                 }  
  232.                 curl_close ( $done ['handle'] );  
  233.                 //从列队中移除已经完成的request  
  234.                 curl_multi_remove_handle ( $master, $done ['handle'] );  
  235.             }  
  236.             //等待所有cURL批处理中的活动连接  
  237.             if ($active)  
  238.                 curl_multi_select ( $master, $this->timeout );  
  239.         } while ( $active );  
  240.         //完成关闭  
  241.         curl_multi_close ( $master );  
  242.         return true;  
  243.     }  
  244.     /** 
  245.      * 获取没得请求对象的cURL配置 
  246.      * @access private 
  247.      * @param object $request 
  248.      * @return array 
  249.      */  
  250.     private function get_options($request) {  
  251.         $options = $this->__get ( 'options' );  
  252.         if (ini_get ( 'safe_mode' ) == 'Off' || ! ini_get ( 'safe_mode' )) {  
  253.             $options [CURLOPT_FOLLOWLOCATION] = 1;  
  254.             $options [CURLOPT_MAXREDIRS] = 5;  
  255.         }  
  256.         $headers = $this->__get ( 'headers' );  
  257.           
  258.         if ($request->options) {  
  259.             $options = $request->options + $options;  
  260.         }  
  261.           
  262.         $options [CURLOPT_URL] = $request->url;  
  263.           
  264.         if ($request->post_data && strtolower($request->method) == 'post' ) {  
  265.             $options [CURLOPT_POST] = 1;  
  266.             $options [CURLOPT_POSTFIELDS] = $request->post_data;  
  267.         }  
  268.         if ($headers) {  
  269.             $options [CURLOPT_HEADER] = 0;  
  270.             $options [CURLOPT_HTTPHEADER] = $headers;  
  271.         }  
  272.           
  273.         return $options;  
  274.     }  
  275.     /** 
  276.      * 设置错误信息 
  277.      * @access public 
  278.      * @param string $msg 
  279.      */  
  280.     public function set_error($msg) {  
  281.         if (! empty ( $msg ))  
  282.             $this->errors [] = $msg;  
  283.     }  
  284.     /** 
  285.      * 获取错误信息 
  286.      * @access public 
  287.      * @param string $open 
  288.      * @param string $close 
  289.      * @return string 
  290.      */  
  291.     public function display_errors($open = '<p>', $close = '</p>') {  
  292.         $str = '';  
  293.         foreach ( $this->errors as $val ) {  
  294.             $str .= $open . $val . $close;  
  295.         }  
  296.         return $str;  
  297.     }  
  298.     /** 
  299.      * @access public 
  300.      * @param string $name 
  301.      * @param string $value 
  302.      * @return boolean 
  303.      */  
  304.     public function __set($name, $value) {  
  305.         if ($name == 'options' || $name == 'headers') {  
  306.             $this->{$name} = $value + $this->{$name};  
  307.         } else {  
  308.             $this->{$name} = $value;  
  309.         }  
  310.         return TRUE;  
  311.     }  
  312.     /** 
  313.      *  
  314.      * @param string $name 
  315.      * @return mixed 
  316.      * @access public 
  317.      */  
  318.     public function __get($name) {  
  319.         return (isset ( $this->{$name} )) ? $this->{$name} : null;  
  320.     }  
  321.     /** 
  322.      * @return void 
  323.      * @access public 
  324.      */  
  325.     public function __destruct() {  
  326.         unset ( $this->size, $this->timeout, $this->callback, $this->options, $this->headers, $this->requests, $this->request_map, $this->errors );  
  327.     }  
  328. }  
  329. // END Curl Class  
  330. /* End of file curl.class.php */  

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
高并发场景之RabbitMQ篇
上次我们介绍了在单机、集群下高并发场景可以选择的一些方案,传送门:高并发场景之一般解决方案 但是也发现了一些问题,比如集群下使用ConcurrentQueue或加锁都不能解决问题,后来采用Redis队列也不能完全解决问题, 因为使用Redis要自己实现分布式锁   这次我们来了解一下一个专门处理队列的组件:RabbitMQ,这个东西天生支持分布式队列。
1212 0
31、深入理解计算机系统笔记,并发编程(concurrent)(3)
1、基于预线程化(prethreading)的并发服务器 常规的并发服务器中,我们为每一个客户端创建一个新线程,代价较大。一个基于预线程化的服务器通过使用“生产者-消费者模型”来试图降低这种开销。
1022 0
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
23539 0
PHP中利用文件锁实现日志写入和网站接口访问等常见场景下的并发控制
针对并发环境下网站、日志文件写入产生的脏数据、更新丢失等情况的解决思路之一
2734 0
【高并发】导致并发编程频繁出问题的“幕后黑手”
工作了3年的小菜同学,平时在公司只是做些CRUD的常规工作,这次,出去面试被面试官一顿虐啊!尤其是并发编程的知识简直就是被吊打啊。小菜心有不甘,回来找自己工作经验非常丰富的朋友大冰来帮助自己提升并发编程的知识,于是便有了接下来的一系列小菜学并发的文章。
148 0
java面试-Java并发编程(八)——闭锁、同步屏障、信号量详解
1. 闭锁:CountDownLatch 1.1 使用场景 若有多条线程,其中一条线程需要等到其他所有线程准备完所需的资源后才能运行,这样的情况可以使用闭锁。
1201 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
20203 0
java并发笔记之synchronized 偏向锁 轻量级锁 重量级锁证明
java并发笔记之synchronized 偏向锁 轻量级锁 重量级锁证明本篇将从hotspot源码(64 bits)入手,通过分析java对象头引申出锁的状态;本文采用大量实例及分析,请耐心看完,谢谢 先来看一下hotspot的源码当中的对象头的注释(32bits 可以忽略了,现在基本没有32位...
1151 0
30、深入理解计算机系统笔记,并发编程(concurrent)(2)
1、共享变量 1)线程存储模型 线程由内核自动调度,每个线程都有它自己的线程上下文(thread context),包括一个惟一的整数线程ID(Thread ID,TID),栈,栈指针,程序计数器,通用目的寄存器和条件码。
734 0
+关注
514
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载