当前位置: 首页> 最新文章列表> curl_multi_close 在请求过程中出现中断的应对方法

curl_multi_close 在请求过程中出现中断的应对方法

gitbox 2025-05-29

在 PHP 中使用 curl_multi 函数时,可能会遇到一个常见的情况:在多请求并行处理的过程中,curl_multi_close() 被调用后,可能会中断当前正在进行的请求。此问题有时会导致程序无法顺利完成所有并发请求,因此需要采取适当的措施来避免这一问题。本文将介绍如何有效应对 curl_multi_close 在请求过程中出现中断的问题。

什么是 curl_multi_close?

curl_multi_close() 是 PHP 的 cURL 扩展提供的一个函数,用于关闭通过 curl_multi_init() 创建的 cURL 多重句柄。它的作用是释放资源,但如果在请求过程中提前调用该函数,就有可能中断尚未完成的请求。这样可能导致数据丢失或请求不完全,从而影响系统的稳定性和性能。

代码示例

以下是一个简单的 curl_multi 请求示例:

<?php
// 初始化 curl_multi 句柄
$mh = curl_multi_init();

// 创建多个 cURL 句柄
$ch1 = curl_init("http://gitbox.net/api/data1");
$ch2 = curl_init("http://gitbox.net/api/data2");
curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);

// 将每个 cURL 句柄添加到 multi 句柄中
curl_multi_add_handle($mh, $ch1);
curl_multi_add_handle($mh, $ch2);

// 执行所有的请求
$running = null;
do {
    $mrc = curl_multi_exec($mh, $running);
} while ($running > 0);

// 获取返回数据
$response1 = curl_multi_getcontent($ch1);
$response2 = curl_multi_getcontent($ch2);

// 关闭 cURL 句柄
curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);

// 关闭 multi 句柄
curl_multi_close($mh);
?>

在这个例子中,我们通过 curl_multi_add_handle() 将多个 cURL 句柄添加到多重句柄 $mh 中,并通过 curl_multi_exec() 并行执行所有请求。最终,我们通过 curl_multi_remove_handle()curl_multi_close() 关闭多重句柄。

问题的产生

问题出现在调用 curl_multi_close() 时,可能会提前关闭多重句柄,从而导致请求中途被中断。若中断发生在所有请求还未完成时,未完成的请求将无法获取响应数据或返回结果。

如何避免中断问题?

为了确保 curl_multi_close() 不会中断请求,可以采用以下几种方法:

1. 确保所有请求完成后再调用 curl_multi_close()

最常见的解决方法是确保所有请求都已完成后,再调用 curl_multi_close()。可以使用 curl_multi_exec() 的返回值来判断是否所有请求都已完成。当 running 变量的值为 0 时,表示所有请求已完成,这时才调用 curl_multi_close()

修改后的代码如下:

<?php
// 初始化 curl_multi 句柄
$mh = curl_multi_init();

// 创建多个 cURL 句柄
$ch1 = curl_init("http://gitbox.net/api/data1");
$ch2 = curl_init("http://gitbox.net/api/data2");
curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);

// 将每个 cURL 句柄添加到 multi 句柄中
curl_multi_add_handle($mh, $ch1);
curl_multi_add_handle($mh, $ch2);

// 执行所有的请求
$running = null;
do {
    $mrc = curl_multi_exec($mh, $running);
    // 可以在这里添加一个小的延时以减缓 CPU 使用率
    usleep(100);
} while ($running > 0);

// 获取返回数据
$response1 = curl_multi_getcontent($ch1);
$response2 = curl_multi_getcontent($ch2);

// 关闭 cURL 句柄
curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);

// 关闭 multi 句柄
curl_multi_close($mh);
?>

通过 do-while 循环持续执行请求,直到 running 为 0,此时所有请求都已完成,可以安全地调用 curl_multi_close()

2. 捕获错误并进行重试

有时候,即使所有请求看似都已经完成,也可能由于网络故障等问题出现中断。这时,可以考虑在请求失败时进行重试。

<?php
// 设置最大重试次数
$maxRetries = 3;

function performRequest($ch, $maxRetries) {
    $retryCount = 0;
    $response = null;
    while ($retryCount < $maxRetries) {
        $response = curl_exec($ch);
        if ($response !== false) {
            break;
        }
        $retryCount++;
        usleep(500000); // 等待一段时间后重试
    }
    return $response;
}

// 初始化 curl_multi 句柄
$mh = curl_multi_init();

// 创建多个 cURL 句柄
$ch1 = curl_init("http://gitbox.net/api/data1");
$ch2 = curl_init("http://gitbox.net/api/data2");
curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);

// 将每个 cURL 句柄添加到 multi 句柄中
curl_multi_add_handle($mh, $ch1);
curl_multi_add_handle($mh, $ch2);

// 执行所有的请求
$running = null;
do {
    $mrc = curl_multi_exec($mh, $running);
} while ($running > 0);

// 获取返回数据,进行重试
$response1 = performRequest($ch1, $maxRetries);
$response2 = performRequest($ch2, $maxRetries);

// 关闭 cURL 句柄
curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);

// 关闭 multi 句柄
curl_multi_close($mh);
?>

在上述代码中,performRequest() 函数会尝试在请求失败时进行重试,直到达到最大重试次数为止。这样可以有效地避免由于网络问题造成的请求中断。

3. 使用 curl_multi_select()

curl_multi_select() 可以帮助 PHP 程序更高效地处理多个请求,避免在每次循环中都调用 curl_multi_exec()。它会在某些条件满足时才会继续执行请求,这样可以节省系统资源。

<?php
// 初始化 curl_multi 句柄
$mh = curl_multi_init();

// 创建多个 cURL 句柄
$ch1 = curl_init("http://gitbox.net/api/data1");
$ch2 = curl_init("http://gitbox.net/api/data2");
curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true);

// 将每个 cURL 句柄添加到 multi 句柄中
curl_multi_add_handle($mh, $ch1);
curl_multi_add_handle($mh, $ch2);

// 执行所有的请求
$running = null;
do {
    curl_multi_select($mh);
    $mrc = curl_multi_exec($mh, $running);
} while ($running > 0);

// 获取返回数据
$response1 = curl_multi_getcontent($ch1);
$response2 = curl_multi_getcontent($ch2);

// 关闭 cURL 句柄
curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);

// 关闭 multi 句柄
curl_multi_close($mh);
?>

curl_multi_select() 可以在没有可用数据时进行等待,从而避免无谓的 CPU 占用。它是一个更高效的方式,尤其是在有大量请求时。