在 PHP 中使用 curl_multi 函数时,可能会遇到一个常见的情况:在多请求并行处理的过程中,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() 不会中断请求,可以采用以下几种方法:
最常见的解决方法是确保所有请求都已完成后,再调用 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()。
有时候,即使所有请求看似都已经完成,也可能由于网络故障等问题出现中断。这时,可以考虑在请求失败时进行重试。
<?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() 函数会尝试在请求失败时进行重试,直到达到最大重试次数为止。这样可以有效地避免由于网络问题造成的请求中断。
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 占用。它是一个更高效的方式,尤其是在有大量请求时。