php curl批处理实现可控并发异步操作示例


Posted in PHP onMay 09, 2018

本文实例讲述了php curl批处理实现可控并发异步操作。分享给大家供大家参考,具体如下:

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

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

<?php
include 'curl.class.php';
function callback($response, $info, $error, $request)
{
 echo 'response:<br>';
 print_r($response);
 echo '<br>' . date("Y-m-d H:i:s") . '   <br>';
 echo '<br>' . str_repeat("-", 100) . '<br>';
}
$USER_COOKIE = (!empty($_REQUEST['cookie'])) ? $_REQUEST['cookie'] : file_get_contents("cookie.txt");
$curl = new Curl ("callback");
$data = array(
 array(
  'url' => 'http://dyactive2.vip.xunlei.com/com_sign/?game=qmr&type=rec_gametime&referfrom=&rt=0.42521539455332336', //秦美人
  'method' => 'POST',
  'post_data' => '',
  'header' => null,
  'options' => array(
   CURLOPT_REFERER => "http://niu.xunlei.com/entergame/?gameNo=qmr&fenQuNum=3",
   CURLOPT_COOKIE => $USER_COOKIE,
  )
 ),
 array(
  'url' => 'http://dyactive2.vip.xunlei.com/com_sign/?game=sq&type=rec_gametime&referfrom=&rt=0.42521539455332336', //神曲
  'method' => 'POST',
  'post_data' => '',
  'header' => null,
  'options' => array(
   CURLOPT_REFERER => "http://niu.xunlei.com/entergame/?gameNo=sq&fenQuNum=41",
   CURLOPT_COOKIE => $USER_COOKIE,
  )
 ),
 array(
  'url' => 'http://dyactive2.vip.xunlei.com/com_sign/?game=frxz&type=rec_gametime&referfrom=&rt=0.42521539455332336', //凡人修真
  'method' => 'POST',
  'post_data' => '',
  'header' => null,
  'options' => array(
   CURLOPT_REFERER => "http://niu.xunlei.com/entergame/?gameNo=frxz&fenQuNum=3",
   CURLOPT_COOKIE => $USER_COOKIE,
  )
 ),
 array(
  'url' => 'http://dyactive2.vip.xunlei.com/com_sign/?game=smxj&type=rec_gametime&referfrom=&rt=0.42521539455332336', //神魔仙界
  'method' => 'POST',
  'post_data' => '',
  'header' => null,
  'options' => array(
   CURLOPT_REFERER => "http://niu.xunlei.com/entergame/?gameNo=smxj&fenQuNum=2",
   CURLOPT_COOKIE => $USER_COOKIE,
  )
 ),
 array(
  'url' => 'http://dyactive2.vip.xunlei.com/com_sign/?game=qsqy&type=rec_gametime&referfrom=&rt=0.42521539455332336', //倾世情缘
  'method' => 'POST',
  'post_data' => '',
  'header' => null,
  'options' => array(
   CURLOPT_REFERER => "http://niu.xunlei.com/entergame/?gameNo=qsqy&fenQuNum=11",
   CURLOPT_COOKIE => $USER_COOKIE,
  )
 ),
);
foreach ($data as $val) {
 $request = new Curl_request ($val ['url'], $val ['method'], $val ['post_data'], $val ['header'], $val ['options']);
 $curl->add($request);
}
$curl->execute();
echo $curl->display_errors();

使用下来效果很好,没有副作用,并发数可控,应用之处多多,自己发挥想象吧

<?php
/**
 * cURL批量处理 工具类
 * 
 * @since Version 1.0
 * @author Justmepzy <justmepzy@gmail.com>
 * @link http://t.qq.com/JustPzy
 */
/**
 *单一的请求对象
 */
class Curl_request {
 public $url   = '';
 public $method   = 'GET';
 public $post_data  = null;
 public $headers  = null;
 public $options  = null;
 /**
  * 
  * @param string $url
  * @param string $method
  * @param string $post_data
  * @param string $headers
  * @param array $options
  * @return void
  */
 public function __construct($url, $method = 'GET', $post_data = null, $headers = null, $options = null) {
  $this->url = $url;
  $this->method = strtoupper( $method );
  $this->post_data = $post_data;
  $this->headers = $headers;
  $this->options = $options;
 }
 /**
  * @return void
  */
 public function __destruct() {
  unset ( $this->url, $this->method, $this->post_data, $this->headers, $this->options );
 }
}
/**
 * 包含请求列队处理
 */
class Curl {
 /**
  * 请求url个数
  * @var int
  */
 private $size    = 5;
 /**
  * 等待所有cURL批处理中的活动连接等待响应时间
  * @var int
  */
 private $timeout   = 5;
 /**
  * 完成请求回调函数
  * @var string
  */
 private $callback   = null;
 /**
  * cRUL配置
  * @var array
  */
 private $options   = array (CURLOPT_SSL_VERIFYPEER => 0,CURLOPT_RETURNTRANSFER => 1,CURLOPT_CONNECTTIMEOUT => 30 );
 /**
  * 请求头
  * @var array
  */
 private $headers   = array ();
 /**
  * 请求列队
  * @var array
  */
 private $requests   = array ();
 /**
  * 请求列队索引
  * @var array
  */
 private $request_map  = array ();
 /**
  * 错误
  * @var array
  */
 private $errors   = array ();
 /**
  * @access public
  * @param string $callback 回调函数
  * 该函数有4个参数($response,$info,$error,$request)
  * $response url返回的body
  * $info  cURL连接资源句柄的信息
  * $error  错误
  * $request  请求对象
  */
 public function __construct($callback = null) {
  $this->callback = $callback;
 }
 /**
  * 添加一个请求对象到列队
  * @access public
  * @param object $request
  * @return boolean
  */
 public function add($request) {
  $this->requests [] = $request;
  return TRUE;
 }
 /**
  * 创建一个请求对象并添加到列队
  * @access public
  * @param string $url
  * @param string $method
  * @param string $post_data
  * @param string $headers
  * @param array $options
  * @return boolean
  */
 public function request($url, $method = 'GET', $post_data = null, $headers = null, $options = null) {
  $this->requests [] = new Curl_request ( $url, $method, $post_data, $headers, $options );
  return TRUE;
 }
 /**
  * 创建GET请求对象
  * @access public
  * @param string $url
  * @param string $headers
  * @param array $options
  * @return boolean
  */
 public function get($url, $headers = null, $options = null) {
  return $this->request ( $url, "GET", null, $headers, $options );
 }
 /**
  * 创建一个POST请求对象
  * @access public
  * @param string $url
  * @param string $post_data
  * @param string $headers
  * @param array $options
  * @return boolean
  */
 public function post($url, $post_data = null, $headers = null, $options = null) {
  return $this->request ( $url, "POST", $post_data, $headers, $options );
 }
 /**
  * 执行cURL
  * @access public
  * @param int $size 最大连接数
  * @return Ambigous <boolean, mixed>|boolean
  */
 public function execute($size = null) {
  if (sizeof ( $this->requests ) == 1) {
   return $this->single_curl ();
  } else {
   return $this->rolling_curl ( $size );
  }
 }
 /**
  * 单个url请求
  * @access private
  * @return mixed|boolean
  */
 private function single_curl() {
  $ch = curl_init ();
  $request = array_shift ( $this->requests );
  $options = $this->get_options ( $request );
  curl_setopt_array ( $ch, $options );
  $output = curl_exec ( $ch );
  $info = curl_getinfo ( $ch );
  // it's not neccesary to set a callback for one-off requests
  if ($this->callback) {
   $callback = $this->callback;
   if (is_callable ( $this->callback )) {
    call_user_func ( $callback, $output, $info, $request );
   }
  } else
   return $output;
  return true;
 }
 /**
  * 多个url请求
  * @access private
  * @param int $size 最大连接数
  * @return boolean
  */
 private function rolling_curl($size = null) {
  if ($size)
   $this->size = $size;
  else 
   $this->size = count($this->requests);
  if (sizeof ( $this->requests ) < $this->size)
   $this->size = sizeof ( $this->requests );
  if ($this->size < 2)
   $this->set_error ( 'size must be greater than 1' );
  $master = curl_multi_init ();
  //添加cURL连接资源句柄到map索引
  for($i = 0; $i < $this->size; $i ++) {
   $ch = curl_init ();
   $options = $this->get_options ( $this->requests [$i] );
   curl_setopt_array ( $ch, $options );
   curl_multi_add_handle ( $master, $ch );
   $key = ( string ) $ch;
   $this->request_map [$key] = $i;
  }
  $active = $done = null;
  do {
   while ( ($execrun = curl_multi_exec ( $master, $active )) == CURLM_CALL_MULTI_PERFORM )
    ;
   if ($execrun != CURLM_OK)
    break;
   //有一个请求完成则回调
   while ( $done = curl_multi_info_read ( $master ) ) {
    //$done 完成的请求句柄
    $info = curl_getinfo ( $done ['handle'] );//
    $output = curl_multi_getcontent ( $done ['handle'] );//
    $error = curl_error ( $done ['handle'] );//
    $this->set_error ( $error );
    //调用回调函数,如果存在的话
    $callback = $this->callback;
    if (is_callable ( $callback )) {
     $key = ( string ) $done ['handle'];
     $request = $this->requests [$this->request_map [$key]];
     unset ( $this->request_map [$key] );
     call_user_func ( $callback, $output, $info, $error, $request );
    }
    curl_close ( $done ['handle'] );
    //从列队中移除已经完成的request
    curl_multi_remove_handle ( $master, $done ['handle'] );
   }
   //等待所有cURL批处理中的活动连接
   if ($active)
    curl_multi_select ( $master, $this->timeout );
  } while ( $active );
  //完成关闭
  curl_multi_close ( $master );
  return true;
 }
 /**
  * 获取没得请求对象的cURL配置
  * @access private
  * @param object $request
  * @return array
  */
 private function get_options($request) {
  $options = $this->__get ( 'options' );
  if (ini_get ( 'safe_mode' ) == 'Off' || ! ini_get ( 'safe_mode' )) {
   $options [CURLOPT_FOLLOWLOCATION] = 1;
   $options [CURLOPT_MAXREDIRS] = 5;
  }
  $headers = $this->__get ( 'headers' );
  if ($request->options) {
   $options = $request->options + $options;
  }
  $options [CURLOPT_URL] = $request->url;
  if ($request->post_data && strtolower($request->method) == 'post' ) {
   $options [CURLOPT_POST] = 1;
   $options [CURLOPT_POSTFIELDS] = $request->post_data;
  }
  if ($headers) {
   $options [CURLOPT_HEADER] = 0;
   $options [CURLOPT_HTTPHEADER] = $headers;
  }
  return $options;
 }
 /**
  * 设置错误信息
  * @access public
  * @param string $msg
  */
 public function set_error($msg) {
  if (! empty ( $msg ))
   $this->errors [] = $msg;
 }
 /**
  * 获取错误信息
  * @access public
  * @param string $open
  * @param string $close
  * @return string
  */
 public function display_errors($open = '<p>', $close = '</p>') {
  $str = '';
  foreach ( $this->errors as $val ) {
   $str .= $open . $val . $close;
  }
  return $str;
 }
 /**
  * @access public
  * @param string $name
  * @param string $value
  * @return boolean
  */
 public function __set($name, $value) {
  if ($name == 'options' || $name == 'headers') {
   $this->{$name} = $value + $this->{$name};
  } else {
   $this->{$name} = $value;
  }
  return TRUE;
 }
 /**
  * 
  * @param string $name
  * @return mixed
  * @access public
  */
 public function __get($name) {
  return (isset ( $this->{$name} )) ? $this->{$name} : null;
 }
 /**
  * @return void
  * @access public
  */
 public function __destruct() {
  unset ( $this->size, $this->timeout, $this->callback, $this->options, $this->headers, $this->requests, $this->request_map, $this->errors );
 }
}
// END Curl Class
/* End of file curl.class.php */

希望本文所述对大家PHP程序设计有所帮助。

PHP 相关文章推荐
PHP中include与require使用方法区别详解
Oct 19 PHP
php的webservice的wsdl的XML无法显示问题的解决方法
Mar 11 PHP
使用PHP导出Redis数据到另一个Redis中的代码
Mar 12 PHP
PhpDocumentor 2安装以及生成API文档的方法
May 21 PHP
Smarty局部缓存的几种方法简介
Jun 17 PHP
PH P5.2至5.5、5.6的新增功能详解
Jul 14 PHP
PHP语法自动检查的Vim插件
Aug 11 PHP
php调用mysql存储过程实例分析
Dec 29 PHP
MySql数据库查询结果用表格输出PHP代码示例
Mar 20 PHP
PHP针对伪静态的注入总结【附asp与Python相关代码】
Aug 01 PHP
TP5框架安全机制实例分析
Apr 05 PHP
PHP使用Redis队列执行定时任务实例讲解
Mar 24 PHP
php使用curl伪造来源ip和refer的方法示例
May 08 #PHP
PHP+ajax实现获取新闻数据简单示例
May 08 #PHP
PHP 计算两个特别大的整数实例代码
May 07 #PHP
详解PHP发送邮件知识点
May 06 #PHP
PHP学习笔记之session
May 06 #PHP
PHP中cookie知识点学习
May 06 #PHP
分析php://output和php://stdout的区别
May 06 #PHP
You might like
PHP数据缓存技术
2007/02/14 PHP
phpcms配置列表页以及获得文章发布时间
2017/07/04 PHP
Laravel事件监听器用法实例分析
2019/03/12 PHP
PHP实现的微信APP支付功能示例【基于TP5框架】
2019/09/16 PHP
jquery实现未经美化的简洁TAB菜单效果
2015/08/28 Javascript
利用jQuery和CSS将背景图片拉伸
2015/10/16 Javascript
JavaScript制作简单的日历效果
2016/03/10 Javascript
jQuery实现鼠标经过购物车出现下拉框代码(推荐)
2016/07/21 Javascript
BootStrap 图标icon符号图标glyphicons不正常显示的快速解决办法
2016/12/08 Javascript
JS实现滑动门效果的方法详解
2016/12/19 Javascript
jQuery弹出层插件popShow用法示例
2017/01/23 Javascript
Angular2开发——组件规划篇
2017/03/28 Javascript
Vue.js实现大转盘抽奖总结及实现思路
2019/10/09 Javascript
vue使用openlayers实现移动点动画
2020/09/24 Javascript
jQuery实现放大镜案例
2020/10/19 jQuery
[04:45]DOTA2上海特级锦标赛主赛事第四日RECAP
2016/03/06 DOTA
六个窍门助你提高Python运行效率
2015/06/09 Python
python中将字典形式的数据循环插入Excel
2018/01/16 Python
python验证码识别教程之利用投影法、连通域法分割图片
2018/06/04 Python
Sanic框架Cookies操作示例
2018/07/17 Python
django drf框架中的user验证以及JWT拓展的介绍
2019/08/12 Python
Python3简单爬虫抓取网页图片代码实例
2019/08/26 Python
解决import tensorflow as tf 出错的原因
2020/04/16 Python
解决Python 函数声明先后顺序出现的问题
2020/09/02 Python
Python 打印自己设计的字体的实例讲解
2021/01/04 Python
CSS3 icon font完全指南(CSS3 font 会取代icon图标)
2013/01/06 HTML / CSS
关于HTML5你必须知道的28个新特性,新技巧以及新技术
2012/05/28 HTML / CSS
澳大利亚最大的网上油画销售画廊:Direct Art Australia
2018/04/15 全球购物
入党积极分子思想汇报范文
2014/01/05 职场文书
工程招投标邀请书
2014/01/30 职场文书
烹调加工管理制度
2014/02/04 职场文书
弄虚作假心得体会
2014/09/10 职场文书
公司员工培训管理制度
2015/08/04 职场文书
sql server 累计求和实现代码
2022/02/28 SQL Server
《异世界四重奏》剧场版6月10日上映 PV视觉图原创角色发表
2022/03/20 日漫
JS前端轻量fabric.js系列物体基类
2022/08/05 Javascript