Код IT
← Каталог

PHP — последовательный curl и curl_multi

Сравнение последовательных задержек и параллельного curl_multi для исходящих HTTP.

PHP main.php
<?php
/**
 * Практикум: https://spirzen.ru/encyclopedia/4-code-dev/4-05-asinhronnost/3
 *
 * Для учебного сравнения без сети используем usleep.
 * Блок curl_multi показывает реальный API для параллельных исходящих запросов.
 */

declare(strict_types=1);

$urls = [
    ['https://example.com/page1', 2_000_000], // мкс
    ['https://example.com/page2', 3_500_000],
    ['https://example.com/page3', 1_500_000],
    ['https://example.com/page4', 2_500_000],
    ['https://example.com/page5', 1_000_000],
];

function downloadSequential(array $urls): float
{
    echo "\n1. ПОСЛЕДОВАТЕЛЬНО (usleep в цикле)\n";
    $start = microtime(true);
    foreach ($urls as [$url, $delayUs]) {
        usleep($delayUs);
        echo "  Готово: {$url}\n";
    }
    $elapsed = microtime(true) - $start;
    printf("  Время: %.2f с\n", $elapsed);
    return $elapsed;
}

function downloadCurlMultiSimulated(array $urls): float
{
    echo "\n2. ПАРАЛЛЕЛЬНО (curl_multi — структура API)\n";
    $start = microtime(true);

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

    foreach ($urls as [$url, $delayUs]) {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT_MS => (int) ($delayUs / 1000) + 500,
            CURLOPT_NOSIGNAL => true,
        ]);
        curl_multi_add_handle($mh, $ch);
        $handles[] = ['ch' => $ch, 'url' => $url, 'delayUs' => $delayUs];
    }

    // Если сеть недоступна — имитируем параллельное ожидание локально
    $running = null;
    do {
        $status = curl_multi_exec($mh, $running);
        if ($running > 0) {
            curl_multi_select($mh, 0.05);
        }
        if ($status !== CURLM_OK) {
            break;
        }
    } while ($running > 0);

    foreach ($handles as $item) {
        $body = curl_multi_getcontent($item['ch']);
        if ($body === false || $body === '') {
            usleep($item['delayUs']);
        }
        echo "  Готово: {$item['url']}\n";
        curl_multi_remove_handle($mh, $item['ch']);
        curl_close($item['ch']);
    }

    curl_multi_close($mh);

    $elapsed = microtime(true) - $start;
    printf("  Время: %.2f с\n", $elapsed);
    return $elapsed;
}

echo "=== PHP — sequential vs curl_multi ===\n";

$seq = downloadSequential($urls);
$par = downloadCurlMultiSimulated($urls);

echo "\n--- Итог ---\n";
printf("Последовательно: %.2f с\n", $seq);
printf("curl_multi:      %.2f с\n", $par);
printf("Ускорение:       %.2fx\n", $seq / max($par, 0.001));
<?php
/**
 * Практикум: https://spirzen.ru/encyclopedia/4-code-dev/4-05-asinhronnost/3
 *
 * Для учебного сравнения без сети используем usleep.
 * Блок curl_multi показывает реальный API для параллельных исходящих запросов.
 */

declare(strict_types=1);

$urls = [
    ['https://example.com/page1', 2_000_000], // мкс
    ['https://example.com/page2', 3_500_000],
    ['https://example.com/page3', 1_500_000],
    ['https://example.com/page4', 2_500_000],
    ['https://example.com/page5', 1_000_000],
];

function downloadSequential(array $urls): float
{
    echo "\n1. ПОСЛЕДОВАТЕЛЬНО (usleep в цикле)\n";
    $start = microtime(true);
    foreach ($urls as [$url, $delayUs]) {
        usleep($delayUs);
        echo "  Готово: {$url}\n";
    }
    $elapsed = microtime(true) - $start;
    printf("  Время: %.2f с\n", $elapsed);
    return $elapsed;
}

function downloadCurlMultiSimulated(array $urls): float
{
    echo "\n2. ПАРАЛЛЕЛЬНО (curl_multi — структура API)\n";
    $start = microtime(true);

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

    foreach ($urls as [$url, $delayUs]) {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT_MS => (int) ($delayUs / 1000) + 500,
            CURLOPT_NOSIGNAL => true,
        ]);
        curl_multi_add_handle($mh, $ch);
        $handles[] = ['ch' => $ch, 'url' => $url, 'delayUs' => $delayUs];
    }

    // Если сеть недоступна — имитируем параллельное ожидание локально
    $running = null;
    do {
        $status = curl_multi_exec($mh, $running);
        if ($running > 0) {
            curl_multi_select($mh, 0.05);
        }
        if ($status !== CURLM_OK) {
            break;
        }
    } while ($running > 0);

    foreach ($handles as $item) {
        $body = curl_multi_getcontent($item['ch']);
        if ($body === false || $body === '') {
            usleep($item['delayUs']);
        }
        echo "  Готово: {$item['url']}\n";
        curl_multi_remove_handle($mh, $item['ch']);
        curl_close($item['ch']);
    }

    curl_multi_close($mh);

    $elapsed = microtime(true) - $start;
    printf("  Время: %.2f с\n", $elapsed);
    return $elapsed;
}

echo "=== PHP — sequential vs curl_multi ===\n";

$seq = downloadSequential($urls);
$par = downloadCurlMultiSimulated($urls);

echo "\n--- Итог ---\n";
printf("Последовательно: %.2f с\n", $seq);
printf("curl_multi:      %.2f с\n", $par);
printf("Ускорение:       %.2fx\n", $seq / max($par, 0.001));