PHP curl_multi_select函数

定义和用法

curl_multi_select() 函数用于等待所有cURL多句柄中的活动连接。这个函数在并发处理多个cURL句柄时非常有用,可以避免CPU过度使用。

语法

curl_multi_select ( CurlMultiHandle $multi_handle [, float $timeout = 1.0 ] ) : int

参数

参数 描述
multi_handle curl_multi_init() 返回的 cURL 多个句柄。
timeout 可选。等待响应的时间,以秒为单位。默认是1.0秒。

返回值

成功时返回描述符集合中准备就绪的描述符的数量,如果在任何描述符准备就绪前超时,则返回0,失败时返回-1。

示例

示例 1:基本用法

// 初始化多个cURL句柄
$mh = curl_multi_init();

// 创建两个cURL资源
$ch1 = curl_init();
$ch2 = curl_init();

// 设置URL和相应的选项
curl_setopt_array($ch1, [
    CURLOPT_URL => "http://www.example.com/",
    CURLOPT_HEADER => 0,
    CURLOPT_RETURNTRANSFER => 1
]);

curl_setopt_array($ch2, [
    CURLOPT_URL => "http://www.php.net/",
    CURLOPT_HEADER => 0,
    CURLOPT_RETURNTRANSFER => 1
]);

// 增加两个句柄
curl_multi_add_handle($mh, $ch1);
curl_multi_add_handle($mh, $ch2);

// 执行批处理句柄
$active = null;
do {
    $status = curl_multi_exec($mh, $active);

    // 等待活动连接,避免CPU过度使用
    if ($status > 0) {
        // 有错误发生
        break;
    }

    // 等待活动,超时时间为1秒
    if (curl_multi_select($mh, 1.0) == -1) {
        // 在select调用中发生错误
        usleep(100000); // 等待100毫秒
    }
} while ($active);

// 获取结果
$content1 = curl_multi_getcontent($ch1);
$content2 = curl_multi_getcontent($ch2);

// 关闭所有句柄
curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);
curl_multi_close($mh);

echo "内容1长度: " . strlen($content1) . "\n";
echo "内容2长度: " . strlen($content2) . "\n";

示例 2:并发下载多个文件

function downloadMultipleUrls($urls, $timeout = 10) {
    $mh = curl_multi_init();
    $handles = [];
    $results = [];

    // 为每个URL创建cURL句柄
    foreach ($urls as $key => $url) {
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => $timeout,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_USERAGENT => 'Mozilla/5.0 (compatible; PHP cURL)'
        ]);
        curl_multi_add_handle($mh, $ch);
        $handles[$key] = $ch;
        $results[$key] = null;
    }

    // 执行所有请求
    $active = null;
    do {
        $mrc = curl_multi_exec($mh, $active);

        // 等待活动,避免CPU过度使用
        if ($mrc == CURLM_OK) {
            // 等待活动连接,超时时间500毫秒
            if (curl_multi_select($mh, 0.5) == -1) {
                usleep(10000); // 等待10毫秒
            }
        }
    } while ($active && $mrc == CURLM_OK);

    // 获取所有结果
    foreach ($handles as $key => $ch) {
        $results[$key] = [
            'content' => curl_multi_getcontent($ch),
            'http_code' => curl_getinfo($ch, CURLINFO_HTTP_CODE),
            'error' => curl_error($ch)
        ];
        curl_multi_remove_handle($mh, $ch);
        curl_close($ch);
    }

    curl_multi_close($mh);
    return $results;
}

// 使用示例
$urls = [
    'example' => 'http://www.example.com',
    'php' => 'http://www.php.net',
    'github' => 'http://www.github.com'
];

$results = downloadMultipleUrls($urls);

foreach ($results as $key => $result) {
    echo "{$key}: HTTP {$result['http_code']}, 内容长度: " .
         strlen($result['content']) . "\n";
}

示例 3:带错误处理的并发请求

function concurrentRequestsWithTimeout($urls, $timeout = 5) {
    $mh = curl_multi_init();
    $curlHandles = [];

    foreach ($urls as $id => $url) {
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => $timeout,
            CURLOPT_CONNECTTIMEOUT => 3,
            CURLOPT_FOLLOWLOCATION => true
        ]);
        curl_multi_add_handle($mh, $ch);
        $curlHandles[$id] = $ch;
    }

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

    do {
        $mrc = curl_multi_exec($mh, $active);

        if ($mrc != CURLM_OK) {
            break;
        }

        // 使用select等待活动,避免忙等待
        $selectTimeout = 0.1; // 100毫秒
        if (curl_multi_select($mh, $selectTimeout) === -1) {
            usleep(10000); // 10毫秒
        }

        // 检查是否超时
        if ((time() - $startTime) > $timeout) {
            break;
        }

    } while ($active > 0);

    $results = [];
    foreach ($curlHandles as $id => $ch) {
        $results[$id] = [
            'content' => curl_multi_getcontent($ch),
            'error' => curl_error($ch),
            'http_code' => curl_getinfo($ch, CURLINFO_HTTP_CODE)
        ];
        curl_multi_remove_handle($mh, $ch);
        curl_close($ch);
    }

    curl_multi_close($mh);
    return $results;
}

注意事项

提示:在PHP 5.5.0之前,curl_multi_select() 在失败时可能会立即返回0而不是等待指定的超时时间。在Windows上,这个函数在PHP 5.5.0之前不能正确工作。
性能建议:使用 curl_multi_select() 可以显著减少并发cURL请求时的CPU使用率,因为它会等待活动连接而不是持续轮询。

常见问题

为什么curl_multi_select返回-1?

curl_multi_select() 返回-1时,通常意味着在select系统调用中发生了错误。在这种情况下,建议等待一小段时间(如100毫秒)后继续,以避免忙等待。

相关函数