$urls =
array(
'http://www.cnblogs.com',
'http://www.google.com.hk',
'http://www.baidu.com',
'http://www.weibo.com',
'http://www.comsenz.com',
'http://www.csdn.net',
);
$opt_arr = array(
CURLOPT_HEADER => FALSE,
CURLOPT_TIMEOUT => 3,
CURLOPT_RETURNTRANSFER => TRUE,
);
// 句柄初始化
$multi = curl_multi_init();
// 参数设置
foreach( $urls as $key => $url) {
$ch_arr[ $key] = curl_init(); // 初始化一个curl资源
$opt_arr[CURLOPT_URL] = $url; // 设置url
curl_setopt_array( $ch_arr[ $key], $opt_arr); // 参数设置
curl_multi_add_handle( $multi, $ch_arr[ $key]); // 加入句柄队列
}
// 预定义一个状态变量
$isrunning = NULL;
// 执行批处理句柄
do {
$mrc = curl_multi_exec( $multi, $isrunning); // $isrunning 一个用来判断操作是否仍在执行的标识的引用。
} while( $mrc == CURLM_CALL_MULTI_PERFORM); // 常量 CURLM_CALL_MULTI_PERFORM 代表还有一些刻不容缓的工作要做
while( $isrunning && $mrc == CURLM_OK) {
if(curl_multi_select( $multi) != -1) { // curl_multi_select阻塞直到cURL批处理连接中有活动连接,失败时返回-1
do {
$mrc = curl_multi_exec( $multi, $isrunning);
} while( $mrc == CURLM_CALL_MULTI_PERFORM);
}
}
// 所有请求接收完之后进行数据的解析等后续处理
foreach( $ch_arr as $key => $ch) {
// 获取内容进行后续处理
$contents = curl_multi_getcontent( $ch);
// do something to deal data
curl_multi_remove_handle( $multi, $ch_arr[ $key]); // 关闭句柄
curl_close( $ch);
'http://www.cnblogs.com',
'http://www.google.com.hk',
'http://www.baidu.com',
'http://www.weibo.com',
'http://www.comsenz.com',
'http://www.csdn.net',
);
$opt_arr = array(
CURLOPT_HEADER => FALSE,
CURLOPT_TIMEOUT => 3,
CURLOPT_RETURNTRANSFER => TRUE,
);
// 句柄初始化
$multi = curl_multi_init();
// 参数设置
foreach( $urls as $key => $url) {
$ch_arr[ $key] = curl_init(); // 初始化一个curl资源
$opt_arr[CURLOPT_URL] = $url; // 设置url
curl_setopt_array( $ch_arr[ $key], $opt_arr); // 参数设置
curl_multi_add_handle( $multi, $ch_arr[ $key]); // 加入句柄队列
}
// 预定义一个状态变量
$isrunning = NULL;
// 执行批处理句柄
do {
$mrc = curl_multi_exec( $multi, $isrunning); // $isrunning 一个用来判断操作是否仍在执行的标识的引用。
} while( $mrc == CURLM_CALL_MULTI_PERFORM); // 常量 CURLM_CALL_MULTI_PERFORM 代表还有一些刻不容缓的工作要做
while( $isrunning && $mrc == CURLM_OK) {
if(curl_multi_select( $multi) != -1) { // curl_multi_select阻塞直到cURL批处理连接中有活动连接,失败时返回-1
do {
$mrc = curl_multi_exec( $multi, $isrunning);
} while( $mrc == CURLM_CALL_MULTI_PERFORM);
}
}
// 所有请求接收完之后进行数据的解析等后续处理
foreach( $ch_arr as $key => $ch) {
// 获取内容进行后续处理
$contents = curl_multi_getcontent( $ch);
// do something to deal data
curl_multi_remove_handle( $multi, $ch_arr[ $key]); // 关闭句柄
curl_close( $ch);
}
curl_multi_close($multi);
注意:CURL在PHP中的多线程处理其实并不是真正的多线程,而是用单线程批处理模拟的多线程效果。
上述代码显然有缺陷:数据处理都是在所有url请求接收完成以后才进行的,如果某些url处理比较慢显然就耽误了整个队列的处理时间,造成了CUP的闲置和浪费,这显然不是我们想要的结果。但是我们可以这样处理:每当一个url请求完成就开始处理,同时等待其它url请求返回。代码如下:
$urls =
array(
'http://www.cnblogs.com',
'http://www.google.com.hk',
'http://www.baidu.com',
'http://www.weibo.com',
'http://www.comsenz.com',
'http://www.csdn.net',
'http://www.php.net',
);
$opt_arr = array(
CURLOPT_HEADER => FALSE,
CURLOPT_TIMEOUT => 5,
CURLOPT_RETURNTRANSFER => TRUE,
);
// 句柄初始化
$multi = curl_multi_init();
// 参数设置
foreach( $urls as $key => $url) {
$ch_arr[ $key] = curl_init(); // 初始化一个curl资源
$opt_arr[CURLOPT_URL] = $url; // 设置url
curl_setopt_array( $ch_arr[ $key], $opt_arr); // 参数设置
curl_multi_add_handle( $multi, $ch_arr[ $key]); // 加入句柄队列
}
// 预定义一个状态变量
$isrunning = NULL;
// 执行批处理句柄
do {
while(( $mrc = curl_multi_exec( $multi, $isrunning)) == CURLM_CALL_MULTI_PERFORM); // $isrunning 一个用来判断操作是否仍在执行的标识的引用。
if( $mrc != CURLM_OK)
break;
while( $done = curl_multi_info_read( $multi)) { // 成功时返回相关信息的数组,失败时返回FALSE
$key = array_search( $done['handle'], $ch_arr);
if(curl_getinfo( $done['handle'], CURLINFO_HTTP_CODE) == '200') {
$content = curl_multi_getcontent( $done['handle']);
// deal $content
echo ++ $j,": $urls[ $key] : ", strlen( $content),"\n";
curl_multi_remove_handle( $multi, $done['handle']);
curl_close( $done['handle']);
} else {
echo ++ $j,": ", $urls[ $key],": ",curl_error( $done['handle']),"\n";
}
}
} while( $isrunning);
curl_multi_close( $multi);
'http://www.cnblogs.com',
'http://www.google.com.hk',
'http://www.baidu.com',
'http://www.weibo.com',
'http://www.comsenz.com',
'http://www.csdn.net',
'http://www.php.net',
);
$opt_arr = array(
CURLOPT_HEADER => FALSE,
CURLOPT_TIMEOUT => 5,
CURLOPT_RETURNTRANSFER => TRUE,
);
// 句柄初始化
$multi = curl_multi_init();
// 参数设置
foreach( $urls as $key => $url) {
$ch_arr[ $key] = curl_init(); // 初始化一个curl资源
$opt_arr[CURLOPT_URL] = $url; // 设置url
curl_setopt_array( $ch_arr[ $key], $opt_arr); // 参数设置
curl_multi_add_handle( $multi, $ch_arr[ $key]); // 加入句柄队列
}
// 预定义一个状态变量
$isrunning = NULL;
// 执行批处理句柄
do {
while(( $mrc = curl_multi_exec( $multi, $isrunning)) == CURLM_CALL_MULTI_PERFORM); // $isrunning 一个用来判断操作是否仍在执行的标识的引用。
if( $mrc != CURLM_OK)
break;
while( $done = curl_multi_info_read( $multi)) { // 成功时返回相关信息的数组,失败时返回FALSE
$key = array_search( $done['handle'], $ch_arr);
if(curl_getinfo( $done['handle'], CURLINFO_HTTP_CODE) == '200') {
$content = curl_multi_getcontent( $done['handle']);
// deal $content
echo ++ $j,": $urls[ $key] : ", strlen( $content),"\n";
curl_multi_remove_handle( $multi, $done['handle']);
curl_close( $done['handle']);
} else {
echo ++ $j,": ", $urls[ $key],": ",curl_error( $done['handle']),"\n";
}
}
} while( $isrunning);
curl_multi_close( $multi);
上述代码仍然是有缺陷的,不知道聪明的读者您发现没有?当URL队列很大时(比如1000),这就是一个大并发显然不合理的,参考这里
function rolling_curl(
$urls,
$callback,
$custom_options =
null) {
$rolling_window = 5;
$rolling_window = ( sizeof( $urls) < $rolling_window) ? sizeof( $urls) : $rolling_window;
$master = curl_multi_init();
$curl_arr = array();
// 设置curl参数
$std_options = array(CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5);
$options = ( $custom_options) ? ( $std_options + $custom_options) : $std_options;
// 初始化curl资源队列
$arr_chs = array();
for ( $i = 0; $i < $rolling_window; $i++) {
$arr_chs[ $urls[ $i]] = curl_init();
$options[CURLOPT_URL] = $urls[ $i];
curl_setopt_array( $arr_chs[ $urls[ $i]], $options);
curl_multi_add_handle( $master, $arr_chs[ $urls[ $i]]);
}
do {
while(( $execrun = curl_multi_exec( $master, $running)) == CURLM_CALL_MULTI_PERFORM);
if( $execrun != CURLM_OK) {
break;
}
while( $done = curl_multi_info_read( $master)) {
$info = curl_getinfo( $done['handle']);
if ( $info['http_code'] == 200) {
$content = curl_multi_getcontent( $done['handle']);
$callback( $content);
// 新建一个curl资源并加入并发队列
if( $i < sizeof( $urls)) {
$arr_chs[ $urls[ $i]] = curl_init();
$options[CURLOPT_URL] = $urls[ $i]; // increment i
curl_setopt_array( $arr_chs[ $urls[ $i]], $options);
curl_multi_add_handle( $master, $arr_chs[ $urls[ $i]]);
}
curl_multi_remove_handle( $master, $done['handle']);
curl_close( $done['handle']);
} else {
echo curl_errno( $done['handle']),":",curl_error( $done['handle']),"\n";
}
}
} while( $running);
curl_multi_close( $master);
return true;
}
$rolling_window = 5;
$rolling_window = ( sizeof( $urls) < $rolling_window) ? sizeof( $urls) : $rolling_window;
$master = curl_multi_init();
$curl_arr = array();
// 设置curl参数
$std_options = array(CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 5);
$options = ( $custom_options) ? ( $std_options + $custom_options) : $std_options;
// 初始化curl资源队列
$arr_chs = array();
for ( $i = 0; $i < $rolling_window; $i++) {
$arr_chs[ $urls[ $i]] = curl_init();
$options[CURLOPT_URL] = $urls[ $i];
curl_setopt_array( $arr_chs[ $urls[ $i]], $options);
curl_multi_add_handle( $master, $arr_chs[ $urls[ $i]]);
}
do {
while(( $execrun = curl_multi_exec( $master, $running)) == CURLM_CALL_MULTI_PERFORM);
if( $execrun != CURLM_OK) {
break;
}
while( $done = curl_multi_info_read( $master)) {
$info = curl_getinfo( $done['handle']);
if ( $info['http_code'] == 200) {
$content = curl_multi_getcontent( $done['handle']);
$callback( $content);
// 新建一个curl资源并加入并发队列
if( $i < sizeof( $urls)) {
$arr_chs[ $urls[ $i]] = curl_init();
$options[CURLOPT_URL] = $urls[ $i]; // increment i
curl_setopt_array( $arr_chs[ $urls[ $i]], $options);
curl_multi_add_handle( $master, $arr_chs[ $urls[ $i]]);
}
curl_multi_remove_handle( $master, $done['handle']);
curl_close( $done['handle']);
} else {
echo curl_errno( $done['handle']),":",curl_error( $done['handle']),"\n";
}
}
} while( $running);
curl_multi_close( $master);
return true;
}