在 PHP 中,cURL 是一个强大的库,它允许我们通过多种协议发送请求,例如 HTTP、HTTPS、FTP 等。而 curl_multi_* 系列函数允许我们同时发出多个异步请求,从而提高性能,减少请求时间,特别是在需要发起多个 HTTP 请求时,异步请求非常有用。
然而,在使用 curl_multi_* 函数进行异步请求时,有一个常见的坑:重复关闭连接。当我们通过 curl_multi_close 函数关闭 cURL 会话时,如果没有正确管理连接,可能会导致一些连接在未被正确关闭前被重复关闭,从而引发错误或内存泄漏。
本文将带您了解如何在 PHP 中正确使用 curl_multi_close 来管理异步请求,并避免重复关闭连接的问题。
首先,我们来看一下如何使用 PHP 的 curl_multi_* 系列函数发起多个异步请求。以下是一个基本示例:
<?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);
// 创建 cURL 多重句柄
$mh = curl_multi_init();
// 将每个 cURL 会话加入多重句柄
curl_multi_add_handle($mh, $ch1);
curl_multi_add_handle($mh, $ch2);
// 执行异步请求
do {
$status = curl_multi_exec($mh, $active);
} while ($active);
// 获取每个请求的返回结果
$response1 = curl_multi_getcontent($ch1);
$response2 = curl_multi_getcontent($ch2);
// 输出结果
echo "Response 1: " . $response1 . "\n";
echo "Response 2: " . $response2 . "\n";
// 关闭会话
curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);
curl_multi_close($mh);
curl_close($ch1);
curl_close($ch2);
?>
在这个例子中,我们创建了两个 cURL 会话并设置了请求 URL,使用 curl_multi_* 函数同时发出两个异步请求。curl_multi_exec 会一直运行,直到所有请求完成。然后我们使用 curl_multi_getcontent 获取返回结果,最后关闭所有会话。
在上面的代码中,我们使用了 curl_multi_close 来关闭 cURL 多重句柄,使用 curl_close 关闭每个单独的 cURL 会话。这是一个常见的做法,但需要注意的是,curl_multi_close 只负责关闭多重句柄,不会关闭每个会话句柄。所以每个 curl_* 会话在关闭 curl_multi_close 后,仍然需要通过 curl_close 来关闭。
问题: 如果你在某些情况下重复调用了 curl_close 或 curl_multi_close,就可能出现连接被重复关闭的问题。为了避免这种情况,我们可以采取以下两种方法:
确保每个句柄只关闭一次
在循环或处理过程中,我们可能会不小心重复关闭某些会话。为避免这种问题,可以在关闭时做一个标记,确保每个句柄只关闭一次。
先移除句柄再关闭
使用 curl_multi_remove_handle 来移除句柄,确保每个句柄在关闭时已从多重句柄中移除。这样可以避免调用 curl_multi_close 时会出现已关闭的句柄。
为了避免重复关闭连接,我们对代码进行改进:
<?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);
// 创建 cURL 多重句柄
$mh = curl_multi_init();
// 将每个 cURL 会话加入多重句柄
curl_multi_add_handle($mh, $ch1);
curl_multi_add_handle($mh, $ch2);
// 执行异步请求
do {
$status = curl_multi_exec($mh, $active);
} while ($active);
// 获取每个请求的返回结果
$response1 = curl_multi_getcontent($ch1);
$response2 = curl_multi_getcontent($ch2);
// 输出结果
echo "Response 1: " . $response1 . "\n";
echo "Response 2: " . $response2 . "\n";
// 移除句柄后再关闭会话
curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);
// 正确关闭
curl_multi_close($mh);
curl_close($ch1);
curl_close($ch2);
?>
使用 curl_multi_* 函数可以有效地发起多个异步请求,提高性能。
在关闭 cURL 会话时,务必要先用 curl_multi_remove_handle 移除会话,然后再调用 curl_multi_close。
每个 cURL 会话都应在关闭前通过 curl_close 关闭,避免在 curl_multi_close 中重复关闭。
通过正确管理连接的关闭,我们能够避免连接被重复关闭,确保代码更加稳定高效。