`
hudeyong926
  • 浏览: 2035407 次
  • 来自: 武汉
社区版块
存档分类
最新评论

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") . '&nbsp;&nbsp;&nbsp;<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 curl批处理实现可控并发异步操作示例

    总之,这个示例展示了如何使用PHP的cURL批处理功能实现可控并发异步操作,这对于需要处理大量URL请求的场景非常有用,例如新闻聚合、商品价格监控和比价应用等。通过这种方式,可以大大提高程序的执行效率,减少等待...

    curl-7.38.0 开源库源码

    3. **多线程支持**:利用curl的multi接口,开发者可以创建多线程或异步的HTTP请求,提高程序的并发性能。 综上所述,curl-7.38.0开源库源码是一个强大的网络通信工具,不仅提供了丰富的协议支持,还具备高度的定制...

    4种PHP异步执行的常用方式.除了ajax,你还知道几种异步调用方式?

    使用Swoole,可以创建异步TCP/UDP服务器,实现真正的并发执行,大大提高PHP处理异步任务的能力。 7. **ReactPHP** ReactPHP是一个基于libevent和libev的PHP异步I/O库,可以构建非阻塞的网络应用。通过EventLoop,...

    php并发解决案例(代码)

    首先,前端并发发出curl请求是一种常见的异步处理方式。CURL(Client URL Library)是PHP中的一个库,用于处理URL和文件传输。通过并发发送多个curl请求,可以显著提高页面加载速度,特别是当需要从不同源获取数据时...

    对curlmulti进行简单地封装处理并行请求

    10. **示例代码**: 假设`hhxsv5-php-multi-curl-4593fe5`是库的源代码,你可以通过阅读和学习这个库的实现来加深理解。它可能包含一个示例,展示如何创建`MultiCurl`对象,添加请求,执行并获取结果。 通过封装`...

    php中的curl_multi系列函数使用例子

    这些函数提供了一种高效的异步处理方式,避免了逐个执行`curl_exec`导致的时间浪费。以下是对这些函数的详细解释: 1. `curl_multi_init()`: 这个函数初始化一个多会话的`curl`句柄,用于存储多个单独的`curl`会话...

    PHP实例开发源码-PHP采集网-英文论坛.zip

    【PHP实例开发源码-PHP采集网-英文论坛.zip】是一个包含PHP编程实践案例的压缩文件,主要用于学习和研究PHP在网页数据采集和处理英文论坛数据方面的应用。在这个压缩包中,我们可以找到一个名为"132699561423274599...

    PHP实现异步调用方法研究与分享

    在本文中,我们将深入探讨如何在PHP中实现异步调用,以便提高程序执行效率和用户体验。异步调用允许后台程序在用户无需等待的情况下独立运行,这对于处理耗时的任务,如发送大量邮件,尤为有用。 传统的HTTP协议是...

    PHP实例开发源码-茉莉QQ机器人源码 php版.zip

    在处理QQ机器人时,可能会遇到需要并发处理多个任务的情况,这时需要理解如何使用异步编程或模拟并发。 8. 文件操作: `使用须知.txt`可能是提供给用户关于如何使用此源码的指南,从中可以学习到PHP的文件读写操作...

    封装了curl,包含管理类,reply类,可用于收发json

    管理类还负责处理`curl`的多线程或多会话操作,例如使用`curl_multi_init`和`curl_multi_perform`来并发执行多个请求。此外,它可能会提供添加、删除、执行请求的方法,并对错误进行处理和日志记录。 其次,“回复...

    基于PHP的葵堆动漫php抓取程序php版源码.zip

    PHP可以通过多线程(pthreads扩展)或异步I/O(比如Swoole扩展)来实现并发处理。 9. 反爬虫策略: 目标网站可能会有反爬虫措施,如验证码、IP限制、User-Agent检查等。抓取程序需要采取相应的策略,如动态设置...

    基于PHP的咖啡蜘蛛池php版源码.zip

    PHP有多种库可供选择,如DOMDocument用于DOM解析,SimpleXML用于XML解析,或者更现代的如Goutte和Symfony DomCrawler组件,它们提供了更友好的API来处理HTML。 3. **正则表达式与字符串处理**:在解析HTML过程中,...

    PHP实例开发源码—星星采集系统 php版.zip

    6. 并发采集:使用curl_multi_init函数,可以实现多线程或异步请求,提高数据采集速度。 7. 错误处理和日志记录:在开发过程中,异常处理和错误日志是必不可少的。try-catch语句块用于捕获和处理异常,error_log...

    PHP实例开发源码—体育php爬虫采集.zip

    7. **异步请求与并发**:为了提高爬虫效率,可能使用cURL或Guzzle等库实现HTTP请求的并发处理,以同时抓取多个页面。 8. **延迟与IP更换策略**:为了避免被目标网站封禁,爬虫需要合理设置请求间隔,可能还需要配合...

    curl_multi_thread_long.rar

    在互联网开发中,高效地处理并发请求是一项关键任务,特别是在大数据传输、实时性要求高的应用中。...通过合理配置和管理线程池,可以优化系统资源利用率,提高整体性能,是现代互联网应用不可或缺的技术手段。

    基于PHP的iWebCrawler搜索爬虫加速工具 php版.zip

    4. **并发与多线程**:为了提高爬取速度,iWebCrawler可能会利用PHP的多进程或多线程特性,比如pthreads扩展,或者使用Guzzle等HTTP客户端库来实现异步请求。 5. **代理和IP池**:为了避免被目标网站封锁,爬虫可能...

    php订单管理系统 php后台订单下单功能

    10. **性能优化**:对于高并发场景,可使用缓存技术(如Redis)存储热门数据,减少数据库查询压力。此外,合理的索引设计、批量处理和异步任务处理也有助于提高系统性能。 综上所述,构建一个PHP订单管理系统需要...

    PHP实例开发源码——EJCMS PHP极品美图爬虫程序特别版.zip

    同时,多线程或异步处理可提高爬虫效率,但需注意不要违反网站的robots.txt规则。 9. **Cookie和Session管理**:某些网站可能需要登录才能访问,这时就需要处理Cookie和Session。PHP提供相应的函数来管理这些会话...

    主机域名PHP多功能域名查询系统-phpchaxun

    这需要使用PHP的`dns_get_record()`函数或cURL库来发送HTTP请求到DNS服务器,获取域名的A记录(IP地址)、MX记录(邮件服务器)、CNAME记录(别名)等信息。 2. **数据库交互**:为了存储和管理查询结果,系统通常...

Global site tag (gtag.js) - Google Analytics