ZetCode

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 请求。

basic_parallel.php
<?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 句柄。我们将它们添加到多句柄并并行执行它们。循环将一直持续到所有请求完成。最后,我们检索并显示响应。

处理多个响应

此示例展示了如何正确处理多个并行请求的响应。

multiple_responses.php
<?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 请求的正确错误处理。

error_handling.php
<?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 错误并将适当的消息存储起来。超时选项可防止在无响应服务器上挂起。结果会连同成功/错误状态一起收集。

控制并发

此示例展示了如何使用队列控制并行请求的数量。

concurrency_control.php
<?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 个)处理,以保持最佳性能而不使服务器过载。在开始新的请求之前,会处理并移除已完成的请求。

处理已完成的响应

此示例演示了立即处理已完成的响应。

streaming_processing.php
<?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 来检测已完成的请求。此方法适用于处理大量具有不同响应时间的请求。

最佳实践

来源

PHP curl_multi_exec 文档

本教程涵盖了 PHP curl_multi_exec 函数,并提供了实际示例,展示了并行请求执行和响应处理。

作者

我叫 Jan Bodnar,是一名充满激情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在教学编程方面拥有十多年的经验。

所有 PHP cURL 教程列表。