前言
使用PHP的curl函数调用多个外部HTTP接口时,默认情况下是需要一个一个执行的,第一个请求结束后才会发起第二个请求,这样会显得效率低。如果各个请求之间没有强依赖关系,可以使用curl的multi系列函数来同时发起多个请求,这样可以节省很多时间,提高效率。
有很多资料说curl_multi是使用多线程来实现并发的,但事实上并不是,它仍然是单线程,只是用了系统的select I/O多路复用机制来实现并发。
测试环境
PHP 7.3
代码
<?php
function main() {
$requests = [
[
'url' => 'https://www.baidu.com', // 请求URL
'method' => 'GET', // 请求方法,支持GET和POST
'header' => [], // 请求头
'param' => ['i' => 0], // 请求参数
],
[
'url' => 'http://www.sdhaidhoasdhaihd.com',
'method' => 'GET',
'header' => [],
'param' => ['i' => 1],
],
];
$handles = [];
foreach ($requests as $req) {
$options = [
CURLOPT_HEADER => false,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
];
if (!empty($req['header'])) {
$options[CURLOPT_HTTPHEADER] = $req['header'];
}
switch ($req['method']) {
case 'GET':
$options[CURLOPT_URL] = "{$req['url']}?" . http_build_query($req['param']);
break;
case 'POST':
$options[CURLOPT_URL] = $req['url'];
$options[CURLOPT_POST] = true;
$options[CURLOPT_POSTFIELDS] = http_build_query($req['param']);
break;
default:
exit("Unsupported method:{$req['method']}");
}
$ch = curl_init();
curl_setopt_array($ch, $options);
$handles[] = $ch;
}
$mh = curl_multi_init();
foreach ($handles as $ch) {
curl_multi_add_handle($mh, $ch);
}
/* 不断执行,直至所有请求完成 */
do {
$errCode = curl_multi_exec($mh, $running);
usleep(100000); // 睡眠100毫秒,避免循环太快打满CPU
} while ($errCode === CURLM_OK && $running > 0);
/* 读取响应数据 */
if ($errCode !== CURLM_OK) {
echo "Execute error: " . curl_multi_strerror($errCode) . "\n";
} else {
while ($info = curl_multi_info_read($mh)) {
$ch = $info['handle'];
if ($info['result'] != CURLM_OK) {
$httpCode = $info['result'];
$httpBody = curl_error($ch);
} else {
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$httpBody = curl_multi_getcontent($ch);
}
$index = array_search($ch, $handles, true);
$requests[$index]['response'] = [
'code' => $httpCode,
'body' => $httpBody,
];
}
}
foreach ($handles as $ch) {
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
curl_multi_close($mh);
/* 打印结果 */
var_dump($requests);
}
main();