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
函数,并通过实际示例展示了各种场景下的并行请求处理。