ZetCode

PHP curl_multi_init 函数

最后修改日期:2025 年 4 月 11 日

PHP 的 curl_multi_init 函数创建一个多 cURL 句柄。它允许异步处理多个 cURL 句柄。这对于高效地进行并行 HTTP 请求非常有用。

基本定义

curl_multi_init 函数初始化一个新的多 cURL 句柄。它返回一个可以与其他 curl_multi 函数一起使用的资源。此句柄管理多个单独的 cURL 句柄。

语法:curl_multi_init(): CurlMultiHandle。该函数不接受任何参数,并返回一个多 cURL 句柄。完成后务必使用 curl_multi_close() 进行清理。

基本并行请求

此示例演示了两个并行 HTTP GET 请求。

basic_parallel.php
<?php

declare(strict_types=1);

$urls = [
    "https://jsonplaceholder.typicode.com/posts/1",
    "https://jsonplaceholder.typicode.com/posts/2"
];

$mh = curl_multi_init();
$handles = [];

foreach ($urls as $url) {
    $ch = curl_init($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 创建单独的句柄并将它们添加到多句柄中。循环处理所有请求直至完成。最后,我们检索并输出响应。

使用回调处理响应

此示例展示了如何使用回调来处理完成的响应。

callback_processing.php
<?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($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_HEADER, false);
    curl_multi_add_handle($mh, $ch);
    $handles[$i] = $ch;
}

$processed = 0;
do {
    curl_multi_exec($mh, $running);
    while ($info = curl_multi_info_read($mh)) {
        $ch = $info['handle'];
        $content = curl_multi_getcontent($ch);
        $index = array_search($ch, $handles, true);
        
        echo "Response $index: " . substr($content, 0, 50) . "...\n";
        $processed++;
        
        curl_multi_remove_handle($mh, $ch);
        curl_close($ch);
    }
    if ($running) {
        curl_multi_select($mh);
    }
} while ($running || $processed < count($urls));

curl_multi_close($mh);

我们使用 curl_multi_info_read 随着响应可用而处理它们。每个已完成的请求都会触发我们的回调逻辑。这种方法比等待所有请求完成更有效。我们跟踪已处理的请求以确保我们处理所有响应。

为并行请求设置超时

此示例演示了为并行请求设置单个超时。

timeout_handling.php
<?php

declare(strict_types=1);

$urls = [
    ["url" =>  "https://httpbin.org/delay/2", "timeout" =>  3],
    ["url" =>  "https://httpbin.org/delay/5", "timeout" =>  1]
];

$mh = curl_multi_init();
$handles = [];

foreach ($urls as $i =>  $item) {
    $ch = curl_init($item['url']);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $item['timeout']);
    curl_multi_add_handle($mh, $ch);
    $handles[$i] = $ch;
}

$running = null;
do {
    curl_multi_exec($mh, $running);
    curl_multi_select($mh);
} while ($running > 0);

foreach ($handles as $i =>  $ch) {
    $errno = curl_errno($ch);
    if ($errno === CURLE_OPERATION_TIMEDOUT) {
        echo "Request $i timed out\n";
    } else {
        echo "Request $i completed: " . 
             substr(curl_multi_getcontent($ch), 0, 30) . "...\n";
    }
    curl_multi_remove_handle($mh, $ch);
    curl_close($ch);
}

curl_multi_close($mh);

我们为每个请求设置了不同的超时。第二个请求将在 1 秒后超时,而第一个请求将成功完成。我们使用 curl_errno 检查超时错误。这种模式对于处理慢响应非常有用。

处理大量 URL

此示例展示了如何有效地处理具有并发控制的大量 URL。

batch_processing.php
<?php

declare(strict_types=1);

function fetchUrls(array $urls, int $concurrency = 5): array {
    $mh = curl_multi_init();
    $results = [];
    $active = [];
    
    // Initialize first batch
    for ($i = 0; $i < min($concurrency, count($urls)); $i++) {
        $ch = curl_init($urls[$i]);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_multi_add_handle($mh, $ch);
        $active[$i] = true;
    }
    
    $nextUrl = $concurrency;
    $processed = 0;
    
    do {
        curl_multi_exec($mh, $running);
        curl_multi_select($mh);
        
        while ($info = curl_multi_info_read($mh)) {
            $ch = $info['handle'];
            $index = array_search($ch, $active, true);
            
            if ($index !== false) {
                $results[$index] = curl_multi_getcontent($ch);
                curl_multi_remove_handle($mh, $ch);
                curl_close($ch);
                $processed++;
                
                // Add next URL if available
                if ($nextUrl < count($urls)) {
                    $ch = curl_init($urls[$nextUrl]);
                    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
                    curl_multi_add_handle($mh, $ch);
                    $active[$nextUrl] = $ch;
                    $nextUrl++;
                }
            }
        }
    } while ($running || $processed < count($urls));
    
    curl_multi_close($mh);
    return $results;
}

$urls = array_fill(0, 20, "https://jsonplaceholder.typicode.com/posts/1");
$results = fetchUrls($urls, 5);

echo "Fetched " . count($results) . " responses\n";

此实现分批处理 URL 以控制并发。我们维护一个活动连接池并替换已完成的连接。该函数按顺序返回所有响应。这种方法可以防止因过多的并发请求而导致服务器过载。

并行请求中的错误处理

此示例演示了并行请求的全面错误处理。

error_handling.php
<?php

declare(strict_types=1);

$urls = [
    "https://valid.example.com/api",
    "https://invalid.example.com/api",
    "https://timeout.example.com/api"
];

$mh = curl_multi_init();
$handles = [];

foreach ($urls as $i =>  $url) {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 5);
    curl_setopt($ch, CURLOPT_FAILONERROR, true);
    curl_multi_add_handle($mh, $ch);
    $handles[$i] = [
        'handle' =>  $ch,
        'url' =>  $url
    ];
}

$running = null;
do {
    curl_multi_exec($mh, $running);
    curl_multi_select($mh);
} while ($running > 0);

foreach ($handles as $i =>  $item) {
    $ch = $item['handle'];
    $errno = curl_errno($ch);
    
    if ($errno) {
        echo "Request $i failed (" . $item['url'] . "): " . 
             curl_error($ch) . "\n";
    } else {
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        if ($httpCode >= 200 && $httpCode < 300) {
            echo "Request $i succeeded: " . 
                 substr(curl_multi_getcontent($ch), 0, 50) . "...\n";
        } else {
            echo "Request $i returned HTTP $httpCode\n";
        }
    }
    
    curl_multi_remove_handle($mh, $ch);
    curl_close($ch);
}

curl_multi_close($mh);

我们为每个请求实现了健壮的错误检查。代码检查 cURL 错误、HTTP 状态码和超时。每次失败都会记录有关出错内容的详细信息。成功响应会照常处理。

最佳实践

来源

PHP curl_multi_init 文档

本教程介绍了 PHP curl_multi_init 函数,并通过实际示例展示了并行请求处理。这些示例演示了各种实际场景和最佳实践。

作者

我叫 Jan Bodnar,我是一名充满热情的程序员,拥有丰富的编程经验。我自 2007 年起撰写编程文章。迄今为止,我已撰写超过 1,400 篇文章和 8 本电子书。我在教学编程方面拥有十年以上的经验。

所有 PHP cURL 教程列表。