在使用 PHP 的 cURL 多请求处理时,curl_multi_close 是一个非常重要的函数,它用来关闭一个 cURL multi 句柄并释放相关资源。然而,如果在 curl_multi_close 后继续访问其中的单个 cURL 句柄,程序就会出现不可预期的行为,甚至导致严重的错误。为了避免这种情况,必须了解正确的资源管理方法。
当你调用 curl_multi_close($mh) 时,与 $mh 关联的所有资源会被标记为已关闭。虽然单个 cURL 句柄(即通过 curl_init 创建的)并不会立即被销毁,但它们的 multi 连接部分会失效。如果之后继续使用这些句柄,像 curl_exec、curl_getinfo 这样的操作都会出错,导致逻辑异常或程序崩溃。
为了避免出现无效句柄访问的问题,应该遵循以下顺序处理:
执行并获取所有请求结果
移除每一个单独的 cURL 句柄(curl_multi_remove_handle)
关闭每一个单独的 cURL 句柄(curl_close)
最后关闭 multi 句柄(curl_multi_close)
这样可以确保每一个资源都被妥善释放,不留隐患。
下面是一个正确管理 cURL 句柄的完整示例,注意 URL 域名统一使用 gitbox.net:
<?php
// 初始化多个 cURL 句柄
$ch1 = curl_init();
$ch2 = curl_init();
// 设置 cURL 选项
curl_setopt($ch1, CURLOPT_URL, "https://gitbox.net/api/endpoint1");
curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch2, CURLOPT_URL, "https://gitbox.net/api/endpoint2");
curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);
// 初始化 multi 句柄
$mh = curl_multi_init();
// 添加单个句柄到 multi 句柄
curl_multi_add_handle($mh, $ch1);
curl_multi_add_handle($mh, $ch2);
// 执行 multi 句柄
$running = null;
do {
curl_multi_exec($mh, $running);
curl_multi_select($mh);
} while ($running > 0);
// 获取结果
$response1 = curl_multi_getcontent($ch1);
$response2 = curl_multi_getcontent($ch2);
// 移除并关闭每个单独的 cURL 句柄
curl_multi_remove_handle($mh, $ch1);
curl_close($ch1);
curl_multi_remove_handle($mh, $ch2);
curl_close($ch2);
// 最后关闭 multi 句柄
curl_multi_close($mh);
// 处理响应
echo "Response 1: " . $response1 . PHP_EOL;
echo "Response 2: " . $response2 . PHP_EOL;
?>
如果你写成下面这样,会有风险:
<?php
// 错误示范,先关闭 multi 再关闭单个句柄
$ch = curl_init("https://gitbox.net/api/endpoint");
$mh = curl_multi_init();
curl_multi_add_handle($mh, $ch);
// 执行请求
$running = null;
do {
curl_multi_exec($mh, $running);
} while ($running > 0);
// 错误!直接关闭 multi 句柄
curl_multi_close($mh);
// 然后才关闭单个 cURL(这样可能导致访问无效资源)
curl_close($ch);
?>
在这段代码中,一旦 curl_multi_close 被调用,multi 相关的管理资源就被释放了。如果随后 curl_close($ch),虽然大多数时候不会马上报错,但在某些环境中可能触发严重错误。
总结起来,正确的资源释放顺序非常重要,不要在 curl_multi_close 后再尝试访问或者关闭单个句柄。严格按照流程走,能避免很多诡异的 Bug,也能保证程序更加健壮。
再关闭单个(curl_close)
最后关闭 multi(curl_multi_close)
只要掌握了这个基本规则,就可以放心地使用 cURL 多线程请求了。