Который работает быстрее, безголовый браузер или керл? - PullRequest
0 голосов
/ 14 февраля 2019

Мне нужно открывать около 100 000 URL-адресов в день, чтобы изображения и HTML-файлы кэшировались в Cloudflare, поскольку содержимое меняется довольно часто.

Я подозреваю, что Curl, вероятно, будет работать быстрее, чем браузер без головы (Chrome Headlessчерез кукловода)

Кто-нибудь имеет какой-либо опыт с этим или есть лучшие способы сделать это?

Ответы [ 2 ]

0 голосов
/ 16 февраля 2019

Во-первых, я уверен, что curl_multi api libcurl значительно быстрее, чем безголовый браузер.даже если он работает под PHP (язык намного медленнее, чем, скажем, C), я считаю, что он будет быстрее, чем браузер без монитора, но давайте проверим его, сравнив его с использованием кода из https://stackoverflow.com/a/54353191/1067003,

сравните этот PHP-скрипт (используя php curl_multi api, который является оберткой вокруг curl_multi api libcurl)

<?php
declare(strict_types=1);
$urls=array();
for($i=0;$i<100000;++$i){
    $urls[]="http://ratma.net/";
}
validate_urls($urls,500,1000,false,false,false);    
// if return_fault_reason is false, then the return is a simple array of strings of urls that validated.
// otherwise it's an array with the url as the key containing  array(bool validated,int curl_error_code,string reason) for every url
function validate_urls(array $urls, int $max_connections, int $timeout_ms = 10000, bool $consider_http_300_redirect_as_error = true, bool $return_fault_reason) : array
{
    if ($max_connections < 1) {
        throw new InvalidArgumentException("max_connections MUST be >=1");
    }
    foreach ($urls as $key => $foo) {
        if (!is_string($foo)) {
            throw new \InvalidArgumentException("all urls must be strings!");
        }
        if (empty($foo)) {
            unset($urls[$key]); //?
        }
    }
    unset($foo);
    // DISABLED for benchmarking purposes: $urls = array_unique($urls); // remove duplicates.
    $ret = array();
    $mh = curl_multi_init();
    $workers = array();
    $work = function () use (&$ret, &$workers, &$mh, &$return_fault_reason) {
        // > If an added handle fails very quickly, it may never be counted as a running_handle
        while (1) {
            curl_multi_exec($mh, $still_running);
            if ($still_running < count($workers)) {
                break;
            }
            $cms=curl_multi_select($mh, 10);
            //var_dump('sr: ' . $still_running . " c: " . count($workers)." cms: ".$cms);
        }
        while (false !== ($info = curl_multi_info_read($mh))) {
            //echo "NOT FALSE!";
            //var_dump($info);
            {
                if ($info['msg'] !== CURLMSG_DONE) {
                    continue;
                }
                if ($info['result'] !== CURLM_OK) {
                    if ($return_fault_reason) {
                        $ret[$workers[(int)$info['handle']]] = array(false, $info['result'], "curl_exec error " . $info['result'] . ": " . curl_strerror($info['result']));
                    }
                } elseif (CURLE_OK !== ($err = curl_errno($info['handle']))) {
                    if ($return_fault_reason) {
                        $ret[$workers[(int)$info['handle']]] = array(false, $err, "curl error " . $err . ": " . curl_strerror($err));
                    }
                } else {
                    $code = (string)curl_getinfo($info['handle'], CURLINFO_HTTP_CODE);
                    if ($code[0] === "3") {
                        if ($consider_http_300_redirect_as_error) {
                            if ($return_fault_reason) {
                                $ret[$workers[(int)$info['handle']]] = array(false, -1, "got a http " . $code . " redirect, which is considered an error");
                            }
                        } else {
                            if ($return_fault_reason) {
                                $ret[$workers[(int)$info['handle']]] = array(true, 0, "got a http " . $code . " redirect, which is considered a success");
                            } else {
                                $ret[] = $workers[(int)$info['handle']];
                            }
                        }
                    } elseif ($code[0] === "2") {
                        if ($return_fault_reason) {
                            $ret[$workers[(int)$info['handle']]] = array(true, 0, "got a http " . $code . " code, which is considered a success");
                        } else {
                            $ret[] = $workers[(int)$info['handle']];
                        }
                    } else {
                        // all non-2xx and non-3xx are always considered errors (500 internal server error, 400 client error, 404 not found, etcetc)
                        if ($return_fault_reason) {
                            $ret[$workers[(int)$info['handle']]] = array(false, -1, "got a http " . $code . " code, which is considered an error");
                        }
                    }
                }
                curl_multi_remove_handle($mh, $info['handle']);
                assert(isset($workers[(int)$info['handle']]));
                unset($workers[(int)$info['handle']]);
                curl_close($info['handle']);
            }
        }
        //echo "NO MORE INFO!";
    };
    foreach ($urls as $url) {
        while (count($workers) >= $max_connections) {
            //echo "TOO MANY WORKERS!\n";
            $work();
        }
        $neww = curl_init($url);
        if (!$neww) {
            trigger_error("curl_init() failed! probably means that max_connections is too high and you ran out of resources", E_USER_WARNING);
            if ($return_fault_reason) {
                $ret[$url] = array(false, -1, "curl_init() failed");
            }
            continue;
        }
        $workers[(int)$neww] = $url;
        curl_setopt_array($neww, array(
            CURLOPT_NOBODY => 1,
            CURLOPT_SSL_VERIFYHOST => 0,
            CURLOPT_SSL_VERIFYPEER => 0,
            CURLOPT_TIMEOUT_MS => $timeout_ms
        ));
        curl_multi_add_handle($mh, $neww);
        //curl_multi_exec($mh, $unused_here); LIKELY TO BE MUCH SLOWER IF DONE IN THIS LOOP: TOO MANY SYSCALLS
    }
    while (count($workers) > 0) {
        //echo "WAITING FOR WORKERS TO BECOME 0!";
        //var_dump(count($workers));
        $work();
    }
    curl_multi_close($mh);
    return $ret;
}

и сравните его, выполнив то же самое в браузере без головы, смею вас

для справки: ratma.net находится в Канаде, а вот из другого центра обработки данных, но также и из Канады:

foo@foo:/srv/http/default/www# time php foo.php

real    0m32.606s
user    0m19.561s
sys     0m12.991s

он выполнил 100 000 запросов за 32,6 секунды, что означает 3067 запросов в секунду.я на самом деле не проверял, но я ожидаю, что браузер без монитора будет работать значительно хуже.

(ps обратите внимание, что этот скрипт не загружает весь контент, а выдает HTTP-запрос HEAD вместо HTTPПОЛУЧИТЕ запрос, если вы хотите, чтобы он загрузил весь контент, замените CURLOPT_NOBODY=>1 на CURLOPT_WRITEFUNCTION=>function($ch,string $data){return strlen($data);})

0 голосов
/ 15 февраля 2019

Лучший способ принять решение - это протестировать оба, но, исходя из моего общего опыта работы с этим типом автоматизации, curl, вероятно, будет быстрее.

Безголовые браузеры полезны, когда вам необходимо полностью эмулироватьреальный браузер - например, когда вам нужно убедиться, что JavaScript на странице работает, или когда вам нужно проверить DOM, который может динамически обновляться.

Если вам нужно только запросить конкретный ресурс, тогда естьнет необходимости в автономном браузере и простой утилите, такой как curl или HTTPie , с которой легче работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...