在 PHP 中,curl_multi_* 系列函数可以让你并发发送多个 HTTP 请求,大大提高了程序的执行效率。不过,很多人在使用 curl_multi_close 结束多请求会话时,容易忽略一个重要问题:。
如果没有合理控制超时时间,当某个请求长时间无响应时,整个应用可能被卡住,造成非常糟糕的用户体验。本文将通过示例,教你如何在使用 curl_multi_close 时,优雅地实现超时控制。
其实,curl_multi_close 只是用来关闭 curl_multi 句柄的,它本身不控制超时。真正的超时控制应该发生在执行请求(即 curl_multi_exec 和 curl_multi_select)的阶段。
简单说,超时控制靠的是:
在单个 curl 句柄上设置 timeout
在执行循环中自行记录和判断总耗时
下面是一个实际的示例。假设我们有两个请求,分别请求 https://gitbox.net/api/endpoint1 和 https://gitbox.net/api/endpoint2,我们希望所有请求总耗时不能超过 10 秒。
<?php
// 创建两个 cURL 句柄
$ch1 = curl_init('https://gitbox.net/api/endpoint1');
$ch2 = curl_init('https://gitbox.net/api/endpoint2');
// 给每个请求单独设置超时(可选)
curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch1, CURLOPT_TIMEOUT, 8); // 单个请求最多8秒
curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch2, CURLOPT_TIMEOUT, 8);
// 创建一个 cURL 批处理句柄
$mh = curl_multi_init();
// 添加句柄到批处理句柄中
curl_multi_add_handle($mh, $ch1);
curl_multi_add_handle($mh, $ch2);
// 执行批处理
$startTime = microtime(true);
$timeout = 10; // 总超时时间(秒)
do {
$status = curl_multi_exec($mh, $active);
// 如果还有活动的连接
if ($active) {
// 阻塞等待
$n = curl_multi_select($mh, 1.0);
// 如果 select 返回 -1,避免忙等
if ($n === -1) {
usleep(100000); // 睡100毫秒
}
}
// 检查是否超时
if ((microtime(true) - $startTime) > $timeout) {
echo "请求总耗时超过{$timeout}秒,强制中断。\n";
break;
}
} while ($active && $status == CURLM_OK);
// 读取结果
$response1 = curl_multi_getcontent($ch1);
$response2 = curl_multi_getcontent($ch2);
// 关闭句柄
curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);
curl_multi_close($mh);
curl_close($ch1);
curl_close($ch2);
// 打印结果
echo "Response 1: " . substr($response1, 0, 100) . "...\n";
echo "Response 2: " . substr($response2, 0, 100) . "...\n";
?>
CURLOPT_TIMEOUT 控制单个请求的超时;
通过记录 microtime(true),手动控制总执行时间;
使用 curl_multi_select 时要设定适当的超时值(比如 1 秒),避免 CPU 占用过高;
curl_multi_close 只负责清理资源,不会控制任何超时逻辑。
如果你设置了过短的 select 超时时间(例如 0.01秒),系统负载可能升高。
如果 curl_multi_select 返回 -1,意味着没有文件描述符可以等待,需要适当睡眠 (usleep) 避免忙循环。
当超时发生时,最好能主动取消未完成的请求,以避免资源泄漏。