此篇介绍一下php的curl_multi系列函数,用于批量发送http请求。
curl_multi原理
注意:CURL在PHP中的多线程处理其实并不是真正的多线程,而是用单线程批处理模拟的多线程效果。
curl_multi使用步骤
使用curl_multi的步骤总结如下:
- 调用curl_multi_init
- 循环调用curl_multi_add_handle,这里需要注意的是,curl_multi_add_handle的第二个参数是由curl_init而来的子handle
- 持续调用curl_multi_exec
- 根据需要循环调用curl_multi_getcontent获取结果
- 调用curl_multi_remove_handle,并为每个子handle调用curl_close
- 调用curl_multi_close
各函数作用解释
-
curl_multi_init()
初始化一个curl批处理句柄资源。 -
curl_multi_add_handle()
向curl批处理会话中添加单独的curl句柄资源。
curl_multi_add_handle()函数有两个参数,第一个参数表示一个curl批处理句柄资源,第二个参数表示一个单独的curl句柄资源。 -
curl_multi_exec()
解析一个curl批处理句柄,curl_multi_exec()函数有两个参数,第一个参数表示一个批处理句柄资源,第二个参数是一个引用值的参数,表示剩余需要处理的单个的curl句柄资源数量。 -
curl_multi_remove_handle()
移除curl批处理句柄资源中的某个句柄资源,curl_multi_remove_handle()函数有两个参数,第一个参数表示一个curl批处理句柄资源,第二个参数表示一个单独的curl句柄资源。 -
curl_multi_close()
关闭一个批处理句柄资源。 -
curl_multi_getcontent()
在设置了CURLOPT_RETURNTRANSFER的情况下,返回获取的输出的文本流。 -
curl_multi_info_read()
获取当前解析的curl的相关传输信息。
示例代码
<?php
$start_time = microtime(true);
echo "\n";
$k = 10;
while ($k > 0) {
$urls[] = "http://127.0.0.1:8080";
$k = $k - 1;
}
print_r(async_get_urls($urls));
echo "\n";
$end_time = microtime(true);
echo $end_time - $start_time;
echo "\n";
function async_get_urls($urls)
{
if (!is_array($urls))
return false;
$result = [];
$handle = [];
$active = 0;
$mh = curl_multi_init(); // 初始化一个curl批处理句柄资源
foreach($urls as $i => $url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // 返回而非输出
curl_setopt($ch, CURLOPT_TIMEOUT, 1); // 控制每一个请求的超时时间
/*
// POST的数据
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postParams);
*/
curl_multi_add_handle($mh, $ch); // 向curl批处理会话中添加单独的curl句柄资源
$handle[$i] = $ch;
}
// 执行
/* curl_multi_exec在底层调用了libcurl的curl_multi_perform函数。
在curl7.20.0以前,此函数会返回一个CURLM_CALL_MULTI_PERFORM值,代表它希望立刻再一次被调用。所以就有了检查此返回值,再一次调用curl_multi_exec函数的demo。
在7.20.0之后,libcurl把这个工作自己在内部做了,所以就不用应用端再做了,直接调用curl_multi_exec,只检查$still_running参数就行了。但此种用法要注意用usleep或者select优化,避免造成cpu占用过高出现假死 */
/*
do {
curl_multi_exec($mh, $running); // 第二个参数表示剩余需要处理的单个curl句柄资源数量
usleep(250000); // 250000 = 0.25 sec
} while ($running > 0);
*/
do {
$mrc = curl_multi_exec($mh, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM); // CURLM_CALL_MULTI_PERFORM (-1):这意味着你需要再次调用curl_multi_exec(),因为仍有数据可供处理
while ($active && $mrc == CURLM_OK) { // CURLM_OK(0):如文档中所说:“都好了”。这意味着可能有更多的数据,但还没有到。
if (curl_multi_select($mh) != -1) {
do {
$mrc = curl_multi_exec($mh, $active);
} while ($mrc == CURLM_CALL_MULTI_PERFORM);
}
}
// 读取结果
foreach($handle as $i => $ch) {
$content['total_time'] = curl_getinfo($ch)['total_time'];
$content['error'] = curl_error($ch);
$content['data'] = json_decode(curl_multi_getcontent($ch), true);
$result[$i] = (curl_errno($ch) == 0) ? $content : false;
}
// 移除handle
foreach($handle as $ch) {
curl_multi_remove_handle($mh, $ch); // 移除curl批处理句柄资源中的某个句柄资源
}
curl_multi_close($mh); // 关闭批处理句柄资源
return $result;
}