PHP curl_multi_add_handle 函数
最后修改日期:2025 年 4 月 11 日
PHP curl_multi_add_handle 函数将一个 cURL 句柄添加到一组 cURL 句柄中。它用于同时执行多个 HTTP 请求。这使得 Web 请求能够高效地并行处理。
基本定义
curl_multi_add_handle 函数将一个标准的 cURL 句柄添加到多句柄中。成功时返回 0,否则返回 CURLM_XXX 错误之一。
语法:curl_multi_add_handle(CurlMultiHandle $multi_handle, CurlHandle $handle): int。多句柄必须使用 curl_multi_init() 创建。完成后务必使用 curl_multi_remove_handle() 删除句柄。
基本的多个 GET 请求
此示例演示了如何并行执行多个 GET 请求。
<?php
declare(strict_types=1);
$urls = [
'https://jsonplaceholder.typicode.com/posts/1',
'https://jsonplaceholder.typicode.com/posts/2',
'https://jsonplaceholder.typicode.com/posts/3'
];
$mh = curl_multi_init();
$handles = [];
foreach ($urls as $url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_multi_add_handle($mh, $ch);
$handles[] = $ch;
}
$running = null;
do {
curl_multi_exec($mh, $running);
curl_multi_select($mh);
} while ($running > 0);
foreach ($handles as $ch) {
echo curl_multi_getcontent($ch) . "\n";
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
curl_multi_close($mh);
此代码同时获取三个帖子。我们为每个 URL 创建单独的 cURL 句柄,并将它们添加到多句柄中。循环处理所有请求直到完成。最后,我们检索并输出每个响应。
使用回调处理响应
此示例展示了如何使用回调在响应完成时处理它们。
<?php
declare(strict_types=1);
$urls = [
'https://jsonplaceholder.typicode.com/users/1',
'https://jsonplaceholder.typicode.com/users/2',
'https://jsonplaceholder.typicode.com/users/3'
];
$mh = curl_multi_init();
$handles = [];
foreach ($urls as $i => $url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PRIVATE, $i); // Store index
curl_multi_add_handle($mh, $ch);
$handles[$i] = $ch;
}
do {
$status = curl_multi_exec($mh, $running);
if ($running) {
curl_multi_select($mh);
}
while ($info = curl_multi_info_read($mh)) {
$ch = $info['handle'];
$index = curl_getinfo($ch, CURLINFO_PRIVATE);
$content = curl_multi_getcontent($ch);
echo "Response $index: " . substr($content, 0, 50) . "...\n";
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
} while ($running && $status == CURLM_OK);
curl_multi_close($mh);
我们使用 curl_multi_info_read 在响应可用时进行处理。每个句柄通过 CURLOPT_PRIVATE 存储其索引。这使我们能够跟踪每个响应属于哪个请求。响应会立即得到处理。
具有不同数据的 POST 请求
此示例演示了具有不同数据负载的并行 POST 请求。
<?php
declare(strict_types=1);
$posts = [
['title' => 'First Post', 'body' => 'Content 1', 'userId' => 1],
['title' => 'Second Post', 'body' => 'Content 2', 'userId' => 2],
['title' => 'Third Post', 'body' => 'Content 3', 'userId' => 3]
];
$mh = curl_multi_init();
$handles = [];
foreach ($posts as $i => $post) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'https://jsonplaceholder.typicode.com/posts');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($post));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json'
]);
curl_setopt($ch, CURLOPT_PRIVATE, $i);
curl_multi_add_handle($mh, $ch);
$handles[$i] = $ch;
}
do {
$status = curl_multi_exec($mh, $running);
if ($running) {
curl_multi_select($mh);
}
while ($info = curl_multi_info_read($mh)) {
$ch = $info['handle'];
$index = curl_getinfo($ch, CURLINFO_PRIVATE);
$response = curl_multi_getcontent($ch);
echo "Post $index created: " . $response . "\n";
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
} while ($running && $status == CURLM_OK);
curl_multi_close($mh);
我们并行发送三个不同的 POST 请求。每个请求都有唯一的 JSON 数据。响应在完成时进行处理。我们通过 CURLOPT_PRIVATE 来维护请求和响应之间的关联。
并行请求中的错误处理
此示例展示了对多个并发请求的正确错误处理。
<?php
declare(strict_types=1);
$urls = [
'https://jsonplaceholder.typicode.com/posts/1', // Valid
'https://jsonplaceholder.typicode.com/nonexistent', // 404
'https://invalid-url.example.com' // Invalid
];
$mh = curl_multi_init();
$handles = [];
foreach ($urls as $i => $url) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PRIVATE, $i);
curl_setopt($ch, CURLOPT_FAILONERROR, true);
curl_multi_add_handle($mh, $ch);
$handles[$i] = $ch;
}
do {
$status = curl_multi_exec($mh, $running);
if ($running) {
curl_multi_select($mh);
}
while ($info = curl_multi_info_read($mh)) {
$ch = $info['handle'];
$index = curl_getinfo($ch, CURLINFO_PRIVATE);
if ($info['result'] !== CURLE_OK) {
echo "Request $index failed: " . curl_error($ch) . "\n";
} else {
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($httpCode >= 400) {
echo "Request $index returned HTTP $httpCode\n";
} else {
echo "Request $index succeeded\n";
}
}
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
} while ($running && $status == CURLM_OK);
curl_multi_close($mh);
我们演示了如何处理并行请求中的不同类型的错误。CURLOPT_FAILONERROR 有助于检测 HTTP 错误。我们同时检查 cURL 错误和 HTTP 状态码。每个错误都与其请求正确关联。
限制并发请求
此示例展示了如何限制并发请求的数量。
<?php
declare(strict_types=1);
$urls = [
'https://jsonplaceholder.typicode.com/posts/1',
'https://jsonplaceholder.typicode.com/posts/2',
'https://jsonplaceholder.typicode.com/posts/3',
'https://jsonplaceholder.typicode.com/posts/4',
'https://jsonplaceholder.typicode.com/posts/5'
];
$maxConcurrent = 2; // Maximum concurrent requests
$mh = curl_multi_init();
$activeHandles = [];
$allHandles = [];
$processed = 0;
while ($processed < count($urls)) {
// Add new handles until we reach max concurrent or run out of URLs
while (count($activeHandles) < $maxConcurrent && $processed < count($urls)) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $urls[$processed]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_PRIVATE, $processed);
curl_multi_add_handle($mh, $ch);
$activeHandles[] = $ch;
$allHandles[$processed] = $ch;
$processed++;
}
// Process active handles
do {
$status = curl_multi_exec($mh, $running);
if ($running) {
curl_multi_select($mh);
}
while ($info = curl_multi_info_read($mh)) {
$ch = $info['handle'];
$index = curl_getinfo($ch, CURLINFO_PRIVATE);
if ($info['result'] === CURLE_OK) {
echo "Request $index completed: " .
substr(curl_multi_getcontent($ch), 0, 30) . "...\n";
}
// Remove completed handle from active list
$key = array_search($ch, $activeHandles, true);
if ($key !== false) {
unset($activeHandles[$key]);
}
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
} while ($running && $status == CURLM_OK);
}
curl_multi_close($mh);
我们维护一个活动句柄池,数量限制为 $maxConcurrent。随着请求完成,新的请求会被添加到池中。这可以防止服务器因过多的同时请求而过载。这种方法对于大批量处理很有用。
最佳实践
- 句柄限制:一次不要添加过多的句柄。
- 错误检查:验证 curl_multi_add_handle 的返回值。
- 资源清理:始终删除并关闭句柄。
- 并发:需要时限制同时进行的请求。
- 超时:为每个句柄设置 CURLOPT_TIMEOUT。
来源
本教程介绍了 PHP curl_multi_add_handle 函数,并通过实际示例展示了各种场景下的并行请求处理。