当前位置: 首页> 最新文章列表> curl_multi_close 与 curl_getinfo 配合使用时的常见坑

curl_multi_close 与 curl_getinfo 配合使用时的常见坑

gitbox 2025-05-29

在 PHP 中使用 cURL 扩展进行并发 HTTP 请求时,curl_multi_* 函数提供了很好的支持。特别是 curl_multi_closecurl_getinfo 这两个函数,经常需要一起使用来处理多个请求的响应。然而,在实际开发中,开发者可能会遇到一些坑,尤其是在配合使用 curl_getinfo 获取每个请求的响应信息时。如果不小心,可能会导致意想不到的错误或者获取不到正确的信息。

1. 使用 curl_multi_close 后的资源问题

curl_multi_close 用来关闭多个 cURL 会话句柄,在请求完成之后,你需要调用该函数来释放所有的资源。如果你在关闭会话之前使用了 curl_getinfo 获取某个请求的详细信息,可能会遇到一个问题:关闭会话后,这些会话句柄所关联的资源就被销毁了,你再去调用 curl_getinfo 就无法获取正确的响应信息。

// 示例代码:错误的做法
$mh = curl_multi_init();
$ch1 = curl_init("https://gitbox.net/api/v1/resource1");
$ch2 = curl_init("https://gitbox.net/api/v1/resource2");

curl_multi_add_handle($mh, $ch1);
curl_multi_add_handle($mh, $ch2);

do {
    $mrc = curl_multi_exec($mh, $active);
} while ($active);

// 错误:在关闭会话句柄之前调用 curl_getinfo
$info1 = curl_getinfo($ch1);  // 这里会出错,因为会话已经关闭
$info2 = curl_getinfo($ch2);

curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);
curl_multi_close($mh);

在上面的例子中,我们在 curl_multi_close 之前调用了 curl_getinfo,这时返回的信息已经不可用,甚至可能会导致 PHP 错误。

2. 正确的做法:先获取信息,再关闭会话

为了避免上述问题,你应该在调用 curl_multi_close 之前,先使用 curl_getinfo 获取每个请求的响应信息。以下是修改后的正确示范:

// 正确做法
$mh = curl_multi_init();
$ch1 = curl_init("https://gitbox.net/api/v1/resource1");
$ch2 = curl_init("https://gitbox.net/api/v1/resource2");

curl_multi_add_handle($mh, $ch1);
curl_multi_add_handle($mh, $ch2);

do {
    $mrc = curl_multi_exec($mh, $active);
} while ($active);

// 正确:在关闭会话之前获取响应信息
$info1 = curl_getinfo($ch1);  
$info2 = curl_getinfo($ch2);

curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);
curl_multi_close($mh);

// 现在你可以安全地使用 $info1 和 $info2 了

在这个修改后的版本中,我们确保在关闭所有会话句柄之前,先通过 curl_getinfo 获取了每个请求的响应信息。这样可以避免在关闭会话句柄后尝试访问已经释放的资源,导致错误发生。

3. 多次请求时的循环问题

如果你同时发送多个请求,并且希望对每个请求获取响应信息,通常会使用一个循环。这个时候,你需要确保每个请求的 curl_getinfo 都是在 curl_multi_close 之前获取的,否则会发生资源丢失。

// 正确的做法:使用循环逐个获取每个请求的信息
$mh = curl_multi_init();
$channels = [];
$urls = [
    "https://gitbox.net/api/v1/resource1",
    "https://gitbox.net/api/v1/resource2"
];

foreach ($urls as $url) {
    $ch = curl_init($url);
    curl_multi_add_handle($mh, $ch);
    $channels[] = $ch;
}

do {
    $mrc = curl_multi_exec($mh, $active);
} while ($active);

foreach ($channels as $ch) {
    $info = curl_getinfo($ch);  // 在这里逐个获取信息
    // 处理 $info
}

foreach ($channels as $ch) {
    curl_multi_remove_handle($mh, $ch);
}

curl_multi_close($mh);

在这个示例中,我们使用了一个数组来存储每个 cURL 会话句柄,并且在 curl_multi_close 之前逐个获取每个请求的响应信息。这种做法避免了之前提到的问题。

4. 注意并发请求中的顺序

有时候,可能并不是所有请求都会在同一个时间完成。为了避免对未完成请求调用 curl_getinfo,你可以使用 curl_multi_select 函数来检测哪些请求已经完成,确保你只对已完成的请求调用 curl_getinfo

// 更复杂的情况:使用 curl_multi_select 处理完成的请求
$mh = curl_multi_init();
$channels = [];
$urls = [
    "https://gitbox.net/api/v1/resource1",
    "https://gitbox.net/api/v1/resource2"
];

foreach ($urls as $url) {
    $ch = curl_init($url);
    curl_multi_add_handle($mh, $ch);
    $channels[] = $ch;
}

do {
    $mrc = curl_multi_exec($mh, $active);
    if ($active) {
        curl_multi_select($mh);  // 等待某个请求完成
    }
} while ($active);

foreach ($channels as $ch) {
    $info = curl_getinfo($ch);  // 在这里获取已完成请求的信息
    // 处理 $info
}

foreach ($channels as $ch) {
    curl_multi_remove_handle($mh, $ch);
}

curl_multi_close($mh);

使用 curl_multi_select 能够确保在请求完成时及时获取信息,从而避免因等待或超时问题而导致的数据丢失。

总结

在 PHP 中使用 curl_multi_* 函数时,curl_getinfocurl_multi_close 是常用的工具。避免常见坑的关键是:在关闭多会话之前,确保每个会话的响应信息已经通过 curl_getinfo 获取。通过合适的代码结构和请求管理,你可以确保程序的稳定性和正确性。