ZetCode

PHP curl_multi_select 函数

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

PHP curl_multi_select 函数等待任何 cURL multi handle 上的活动。它与 curl_multi_exec 一起使用,以高效处理多个 cURL 请求。此函数有助于避免忙等待。

基本定义

curl_multi_select 函数会一直等待,直到任何 cURL multi handle 上有活动。它返回描述符集中包含的描述符数量,失败时返回 -1。

语法:curl_multi_select(CurlMultiHandle $multi_handle, float $timeout = 1.0): int。timeout 参数指定等待的秒数。timeout 为 0 表示立即返回。

基本多请求处理

本示例演示了 curl_multi_select 与多个 URL 的基本用法。

basic_multi_select.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"
];

$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;
}

$active = null;
do {
    $status = curl_multi_exec($mh, $active);
    if ($status > 0) {
        break; // Error occurred
    }
    curl_multi_select($mh); // Wait for activity
} while ($active);

foreach ($handles as $ch) {
    echo curl_multi_getcontent($ch) . "\n";
    curl_multi_remove_handle($mh, $ch);
    curl_close($ch);
}

curl_multi_close($mh);

此代码同时获取三个帖子。我们创建单独的句柄,将它们添加到 multi handle,然后进行处理。select 调用可防止在执行期间进行 CPU 密集型的忙等待。

自定义超时处理

本示例展示了如何使用自定义超时与 curl_multi_select。

custom_timeout.php
<?php

declare(strict_types=1);

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

$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;
}

$active = null;
$start = microtime(true);

do {
    $status = curl_multi_exec($mh, $active);
    if ($status > 0) {
        break;
    }
    
    // Wait for up to 500ms for activity
    $select = curl_multi_select($mh, 0.5);
    
    if ($select === -1) {
        // Error occurred in select
        usleep(100000); // Sleep briefly to prevent CPU overload
    }
    
} while ($active && (microtime(true) - $start < 5)); // Max 5 seconds

foreach ($handles as $ch) {
    echo "Response length: " . strlen(curl_multi_getcontent($ch)) . "\n";
    curl_multi_remove_handle($mh, $ch);
    curl_close($ch);
}

curl_multi_close($mh);

我们使用 500ms 的超时时间,并实现 5 秒的最大执行时间。代码通过短暂休眠来处理 select 返回的 -1 值。此方法可以更好地控制执行流程。

处理大量请求

本示例演示了如何高效处理大量请求。

large_requests.php
<?php

declare(strict_types=1);

// Generate 10 test URLs
$urls = array_map(function($i) {
    return "https://jsonplaceholder.typicode.com/posts/" . ($i + 1);
}, range(0, 9));

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

// Add all handles at once
foreach ($urls as $url) {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_multi_add_handle($mh, $ch);
    $handles[$url] = $ch;
}

$active = null;
$processed = 0;

do {
    $status = curl_multi_exec($mh, $active);
    
    if ($status === CURLM_OK) {
        // Wait for activity with 100ms timeout
        $select = curl_multi_select($mh, 0.1);
        
        if ($select > 0) {
            // Process completed requests
            while ($info = curl_multi_info_read($mh)) {
                $ch = $info['handle'];
                $url = array_search($ch, $handles, true);
                
                if ($info['result'] === CURLE_OK) {
                    echo "Completed: $url\n";
                } else {
                    echo "Error: $url - " . curl_error($ch) . "\n";
                }
                
                curl_multi_remove_handle($mh, $ch);
                curl_close($ch);
                $processed++;
            }
        }
    }
} while ($active || $processed < count($urls));

curl_multi_close($mh);

此代码高效处理 10 个请求。我们使用 curl_multi_info_read 来单独检查已完成的请求。select 超时设置为 100ms 以实现响应式处理。已完成的请求在检测到时会立即得到处理。

错误处理和超时

本示例展示了 curl_multi_select 的稳健错误处理。

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 $url) {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, 3); // Individual timeout
    curl_multi_add_handle($mh, $ch);
    $handles[] = $ch;
}

$active = null;
$start = time();

do {
    $status = curl_multi_exec($mh, $active);
    
    if ($status > CURLM_OK) {
        echo "Multi error: " . curl_multi_strerror($status) . "\n";
        break;
    }
    
    $select = curl_multi_select($mh, 0.5);
    
    if ($select === -1) {
        echo "Select error occurred\n";
        usleep(100000);
    }
    
    // Global timeout check
    if (time() - $start > 5) {
        echo "Global timeout reached\n";
        break;
    }
    
} while ($active);

foreach ($handles as $ch) {
    $info = curl_getinfo($ch);
    
    if (curl_errno($ch)) {
        echo "Error: " . curl_error($ch) . "\n";
    } else {
        echo "HTTP " . $info['http_code'] . " - " . $info['url'] . "\n";
    }
    
    curl_multi_remove_handle($mh, $ch);
    curl_close($ch);
}

curl_multi_close($mh);

我们实现了多层错误处理。每个请求都有 3 秒的超时时间,整个操作有 5 秒的全局超时时间。我们同时检查 multi 错误和 select 错误,并提供适当的反馈。

高级性能优化

本示例演示了性能优化技术。

performance_optimization.php
<?php

declare(strict_types=1);

$urls = [
    "https://api.example.com/resource1",
    "https://api.example.com/resource2",
    "https://api.example.com/resource3"
];

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

// Configure multi handle for better performance
curl_multi_setopt($mh, CURLMOPT_MAXCONNECTS, 10);
curl_multi_setopt($mh, CURLMOPT_PIPELINING, CURLPIPE_MULTIPLEX);

foreach ($urls as $url) {
    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_ENCODING, 'gzip'); // Enable compression
    curl_setopt($ch, CURLOPT_TCP_NODELAY, true); // Disable Nagle's algorithm
    curl_multi_add_handle($mh, $ch);
    $handles[] = $ch;
}

$active = null;
$processed = 0;
$results = [];

do {
    $status = curl_multi_exec($mh, $active);
    
    if ($status === CURLM_OK) {
        // Use 0 timeout for non-blocking check
        $select = curl_multi_select($mh, 0);
        
        if ($select > 0) {
            while ($info = curl_multi_info_read($mh)) {
                $ch = $info['handle'];
                $results[] = [
                    'content' =>  curl_multi_getcontent($ch),
                    'info' =>  curl_getinfo($ch)
                ];
                curl_multi_remove_handle($mh, $ch);
                curl_close($ch);
                $processed++;
            }
        } else {
            // Do other work here if no immediate activity
            usleep(10000); // 10ms delay
        }
    }
} while ($active || $processed < count($urls));

curl_multi_close($mh);

// Process results
foreach ($results as $result) {
    echo "URL: " . $result['info']['url'] . "\n";
    echo "Time: " . $result['info']['total_time'] . "s\n\n";
}

此优化实现使用了 HTTP/2 多路复用、gzip 压缩和 TCP 优化。我们使用非阻塞 select 并设置 0 超时时间来检查即时活动。代码可以在等待时执行其他工作。

最佳实践

来源

PHP curl_multi_select 文档

本教程涵盖了 PHP curl_multi_select 函数,并通过实际示例展示了其在高效处理多个请求中的用法。

作者

我叫 Jan Bodnar,是一名充满热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。至今,我已撰写了超过 1,400 篇文章和 8 本电子书。我在教授编程方面拥有十多年的经验。

所有 PHP cURL 教程列表。