PHP curl_multi_exec 函数
最后修改日期:2025 年 4 月 11 日
PHP curl_multi_exec
函数并行执行多个 cURL 句柄。它用于同时执行多个 HTTP 请求,在从多个源获取数据时提高性能。
基本定义
curl_multi_exec
函数运行当前 cURL 多句柄的子连接。它异步处理每个句柄,实现并行请求。该函数返回一个在 cURL.h 中定义的 cURL 代码。
语法:curl_multi_exec(CurlMultiHandle $multi_handle, int &$still_running): int
。$still_running
参数用于填充仍运行的句柄数量。该函数应重复调用,直到所有句柄完成。
基本并行请求
此示例演示了向不同 URL 发送两个并行 GET 请求。
<?php declare(strict_types=1); // Create multiple cURL handles $ch1 = curl_init(); $ch2 = curl_init(); curl_setopt($ch1, CURLOPT_URL, "https://jsonplaceholder.typicode.com/posts/1"); curl_setopt($ch1, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch2, CURLOPT_URL, "https://jsonplaceholder.typicode.com/comments/1"); curl_setopt($ch2, CURLOPT_RETURNTRANSFER, true); // Create the multi cURL handle $mh = curl_multi_init(); // Add the handles curl_multi_add_handle($mh, $ch1); curl_multi_add_handle($mh, $ch2); // Execute the handles $running = null; do { curl_multi_exec($mh, $running); curl_multi_select($mh); } while ($running > 0); // Get the responses $response1 = curl_multi_getcontent($ch1); $response2 = curl_multi_getcontent($ch2); // Close the handles curl_multi_remove_handle($mh, $ch1); curl_multi_remove_handle($mh, $ch2); curl_multi_close($mh); echo "Post 1: " . $response1 . "\n\n"; echo "Comment 1: " . $response2;
此代码为不同的 API 端点创建了两个 cURL 句柄。我们将它们添加到多句柄并并行执行它们。循环将一直持续到所有请求完成。最后,我们检索并显示响应。
处理多个响应
此示例展示了如何正确处理多个并行请求的响应。
<?php declare(strict_types=1); $urls = [ "https://jsonplaceholder.typicode.com/posts/1", "https://jsonplaceholder.typicode.com/comments/1", "https://jsonplaceholder.typicode.com/albums/1" ]; $mh = curl_multi_init(); $handles = []; foreach ($urls as $i => $url) { $handles[$i] = curl_init(); curl_setopt($handles[$i], CURLOPT_URL, $url); curl_setopt($handles[$i], CURLOPT_RETURNTRANSFER, true); curl_multi_add_handle($mh, $handles[$i]); } $running = null; do { curl_multi_exec($mh, $running); curl_multi_select($mh); } while ($running > 0); $responses = []; foreach ($handles as $i => $handle) { $responses[$i] = curl_multi_getcontent($handle); curl_multi_remove_handle($mh, $handle); curl_close($handle); } curl_multi_close($mh); foreach ($responses as $i => $response) { echo "Response $i: " . substr($response, 0, 100) . "...\n\n"; }
我们从 URL 数组创建多个 cURL 句柄。并行执行后,我们将所有响应收集到一个数组中。该示例演示了正确的资源清理,并展示了如何有效地处理多个响应。
并行请求中的错误处理
此示例演示了并行 cURL 请求的正确错误处理。
<?php declare(strict_types=1); $urls = [ "https://valid.url/api", "https://invalid.url/api", "https://another.valid.url/api" ]; $mh = curl_multi_init(); $handles = []; foreach ($urls as $i => $url) { $handles[$i] = curl_init(); curl_setopt($handles[$i], CURLOPT_URL, $url); curl_setopt($handles[$i], CURLOPT_RETURNTRANSFER, true); curl_setopt($handles[$i], CURLOPT_TIMEOUT, 10); curl_multi_add_handle($mh, $handles[$i]); } $running = null; do { curl_multi_exec($mh, $running); curl_multi_select($mh); } while ($running > 0); $results = []; foreach ($handles as $i => $handle) { $error = curl_error($handle); if ($error) { $results[$i] = "Error for URL $i: " . $error; } else { $results[$i] = "Success for URL $i: " . substr(curl_multi_getcontent($handle), 0, 50) . "..."; } curl_multi_remove_handle($mh, $handle); curl_close($handle); } curl_multi_close($mh); print_r($results);
此示例包括对每个请求的错误检查。我们在执行后检查 cURL 错误并将适当的消息存储起来。超时选项可防止在无响应服务器上挂起。结果会连同成功/错误状态一起收集。
控制并发
此示例展示了如何使用队列控制并行请求的数量。
<?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 parallel requests $mh = curl_multi_init(); $activeHandles = []; $allResponses = []; $currentIndex = 0; while ($currentIndex < count($urls) || count($activeHandles) > 0) { // Add new requests if under limit while (count($activeHandles) < $maxConcurrent && $currentIndex < count($urls)) { $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $urls[$currentIndex]); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_multi_add_handle($mh, $ch); $activeHandles[$currentIndex] = $ch; $currentIndex++; } // Execute the current batch $running = null; do { curl_multi_exec($mh, $running); curl_multi_select($mh); } while ($running > 0); // Process completed requests foreach ($activeHandles as $i => $ch) { $info = curl_getinfo($ch); if ($info['http_code'] == 200) { $allResponses[$i] = substr(curl_multi_getcontent($ch), 0, 50) . "..."; } else { $allResponses[$i] = "Error for URL $i"; } curl_multi_remove_handle($mh, $ch); curl_close($ch); unset($activeHandles[$i]); } } curl_multi_close($mh); print_r($allResponses);
这个高级示例实现了一个队列系统来控制并发。我们将 URL 分批(每次 2 个)处理,以保持最佳性能而不使服务器过载。在开始新的请求之前,会处理并移除已完成的请求。
处理已完成的响应
此示例演示了立即处理已完成的响应。
<?php declare(strict_types=1); $urls = [ "https://jsonplaceholder.typicode.com/posts/1", "https://jsonplaceholder.typicode.com/comments/1", "https://jsonplaceholder.typicode.com/albums/1", "https://jsonplaceholder.typicode.com/photos/1", "https://jsonplaceholder.typicode.com/todos/1" ]; $mh = curl_multi_init(); $handles = []; foreach ($urls as $i => $url) { $handles[$i] = curl_init(); curl_setopt($handles[$i], CURLOPT_URL, $url); curl_setopt($handles[$i], CURLOPT_RETURNTRANSFER, true); curl_multi_add_handle($mh, $handles[$i]); } $completed = 0; $total = count($urls); do { $status = curl_multi_exec($mh, $running); if ($status === CURLM_OK) { // Check for completed requests while ($info = curl_multi_info_read($mh)) { $handle = $info['handle']; $index = array_search($handle, $handles, true); if ($info['result'] === CURLE_OK) { $response = curl_multi_getcontent($handle); echo "Completed $index: " . substr($response, 0, 50) . "...\n"; } else { echo "Failed $index: " . curl_error($handle) . "\n"; } curl_multi_remove_handle($mh, $handle); curl_close($handle); $completed++; } } if ($running) { curl_multi_select($mh, 0.1); } } while ($running > 0 || $completed < $total); curl_multi_close($mh);
此示例在响应可用时立即处理它们,而不是等待所有请求完成。我们使用 curl_multi_info_read
来检测已完成的请求。此方法适用于处理大量具有不同响应时间的请求。
最佳实践
- 限制并发:控制并行请求以避免服务器过载。
- 错误处理:检查每个请求的状态和错误。
- 资源清理:始终正确移除和关闭句柄。
- 超时:为每个句柄设置 CURLOPT_TIMEOUT。
- 内存管理:为大批量处理即时完成的响应。
来源
本教程涵盖了 PHP curl_multi_exec
函数,并提供了实际示例,展示了并行请求执行和响应处理。