量比较小的时候速度还不错。当请求量增大之后开始考虑使用curl_multi_init获取数据,于是参照RollingCurl.php写了这样一个多线程的函数。注意:post批量请求注意参数和返回值。
/**
* 批量多线程发送http/https请求,支持post批量请求
* @param $url_arr 请求地址一维数组
* @param $postFields post参数二维数组
* @param $http_headers 附加头部参数二维数组
* @param $cache_dir 本地缓存路径
* @param $cache_time 本地缓存过期时间
* @return array 关联数组,其键code为http状态码,键data为请求返回值
*/
function get_multi_api_contents($url_arr, $postFields = array(), $http_headers = array(), $cache_dir = '', $cache_time = 0)
{
$responses = $lost_urls = $map = array();
//仅当设置了$cache_time时才从缓存中读取
foreach($url_arr as $key => $url)
{
if($cache_time)
{
if(empty($postFields))
{
$cache_path = $cache_dir.'/'.md5($url);
}else{
$cache_path = $cache_dir.'/'.md5($url.serialize($postFields));
}
if(file_exists($cache_path) && filemtime($cache_path) + $cache_time > time() && filesize($cache_path) > 0)
{
//如果需要区分从本地缓存和从网络请求可以设置为304或其他http_code
$responses[$url] = array('code'=>200, 'data'=>file_get_contents($cache_path));
}else{
//缓存失效的url缓存
$lost_urls[$key] = array('url' => $url, 'cache' => $cache_path);
}
}else{
$lost_urls[$key] = array('url' => $url, 'cache' => null);
}
}
$queue = curl_multi_init();
foreach($lost_urls as $key => $info)
{
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $info['url']);
curl_setopt($ch, CURLOPT_TIMEOUT, 30);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
if(isset($postFields[$key]))
{
curl_setopt($ch, CURLOPT_POSTFIELDS, $postFields[$key]);
}
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_NOSIGNAL, true);
curl_setopt($ch, CURLOPT_ENCODING, 'gzip');
//和设置单个curl请求方式一样,例如
//$default_browser = '';
//curl_setopt($ch, CURLOPT_USERAGENT, $default_browser);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_MAXREDIRS, 10);
//https 请求
if(strtolower(substr($info['url'], 0, 5)) == 'https')
{
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
}
if(isset($http_headers[$key]))
{
$headers = array();
foreach($http_headers[$key] as $k => $v)
{
$headers[] = "$k: $v";
}
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
}
curl_multi_add_handle($queue, $ch);
$map[(string) $ch] = $info['url'];
}
do {
while(($code = curl_multi_exec($queue, $active)) == CURLM_CALL_MULTI_PERFORM);
if($code != CURLM_OK)
{
break;
}
while($done = curl_multi_info_read($queue))
{
$getinfo = curl_getinfo($done['handle']);
$data = curl_multi_getcontent($done['handle']);
$responses[$map[(string) $done['handle']]] = array('code'=>$getinfo['http_code'], 'data'=>$data);
curl_multi_remove_handle($queue, $done['handle']);
curl_close($done['handle']);
}
if($active > 0)
{
curl_multi_select($queue, 0.5);
}
}while($active);
curl_multi_close($queue);
//更新缓存
if($cache_time && !empty($lost_urls))
{
//填补丢失的缓存
foreach($lost_urls as $info)
{
//仅当http_code为200时写入本地缓存
if($responses[$info['url']]['code'] == 200)
{
file_put_contents($info['cache'], $responses[$info['url']]['data']);
}
}
}
return $responses;
}
测试案例://api链接地址数组,支持https请求,使用post请求相同url时请添加无意义参数,例如?r=1
$url_arr = array(
0 => 'https://img.alicdn.com/tps/TB1sXGYIFXXXXc5XpXXXXXXXXXX.jpg?r=1',
1 => 'https://img.alicdn.com/tps/TB1pfG4IFXXXXc6XXXXXXXXXXXX.jpg?r=2',
2 => 'https://img.alicdn.com/tps/TB1h9xxIFXXXXbKXXXXXXXXXXXX.jpg?r=3',
);
//使用post参数时按照$url_arr对应键名提供
$postFields = array();
//使用自定义http头部的时候按照$url_arr对应键名提供
$http_headers = array();
//api请求缓存路径
$cache_dir = __DIR__ .'/cache';
//api请求结果在本地缓存时间,使用post方式请求时需考虑是否设置缓存时间(默认支持post缓存)
$cache_time = 3600;
$results = get_multi_api_contents($url_arr, $postFields, $http_headers, $cache_dir, $cache_time);
主要是为了其他的多线程代码(非curl多并发)中重复请求相同的url时优先从缓存中读取,减小访问新浪接口次数。
考虑到并发不能太高,获得请求结果之后可以从code中判断http响应值是否为200再决定是否重新请求,而且缓存之间相互独立。不过,该方法的性能肯定没有网上的传的《Curl多线程 | X》那么强大,那篇文章给出的curl类请求号称是充分利用CPU和带宽,且支持回调函数,以后需要的时候肯定能派上用场。
转载随意,但请附上文章地址:-)