Класс MultiCurl

Тема в разделе ".:: Готовые решения", создана пользователем Jeurey, 2 май 2009.

Статус темы:
Закрыта.
  1. Jeurey

    Jeurey

    Регистр.:
    13 сен 2006
    Сообщения:
    419
    Симпатии:
    576
    Хм. Интересно... Дай-ка код или опиши что конкретно пытаешься сделать.
     
  2. KillDead

    KillDead

    Регистр.:
    11 авг 2006
    Сообщения:
    889
    Симпатии:
    556
    В общем тестил на 2-х машинах на пхп версии 5.2. После перехода на 5.3 всё заработало.
    ------------
    Те, кто пользуется этой (да и многими другими обёртками к курлу) заметили что сдесь реализованы блокирующие соединения- скрипт ждёт окончания всех паралельных коннектов и только потом возвращает управление, соответственно если у тебя хоть один сервак не отвечает - вся пачка будет висеть и ждать, пока он не отвалится по таймауту.
    В инете есть реализации неблокирующих соединений, но после попытки использовать их в работе возвратился к данному классу. Тк он стабильнее в работе и в общем лучше написан (как мне показалось).
    Тем более чтобы реализовать необходимое в данном классе необходимы минимальные телодвижения, нужно всего переопределить getUrls() функцию и добавить ещё одну для задания имени callback функции:
    PHP:
        /**
         *  Получение содержимого из списка урлов в массив
         * @return array $results
         */
        
    public function getUrls() {
            if(!
    sizeof($this->_urls)) {
                return 
    self::E_EMPTY_URLS_LIST;
            }
            
    $results    = array();
            while(
    true) {
                
    // Создание потоков
                
    while ($this->_threadsRunning $this->_threadsCount && sizeof($this->_urls)) {  //
                    
    $url_end end($this->_urls);
                    
    $url_end_key key($this->_urls);
                    
    #echo "ADD: {$url_end}\n" ;  flush();
                    
    $this->_createThread();
                    
    end($this->_curlHandles);
                    
    $this->_threadId_connect[key($this->_curlHandles)] = $url_end_key;              
                    
    $this->_threadsRunning++;
                }
                
    // Проверка на выполненность
                
    if ($this->_threadsRunning == && !sizeof($this->_urls) ) {
                    break;
                }
                
    // Let mcurl do it's thing
                
    curl_multi_select($this->_curlMultiHandle);
                while((
    $mcRes curl_multi_exec($this->_curlMultiHandle$mcActive)) == CURLM_CALL_MULTI_PERFORM) {
                    
    usleep($this->_usleepTime);
                }
                if(
    $mcRes != CURLM_OK) {
                    break;
                }
                while(
    $threadInfo curl_multi_info_read($this->_curlMultiHandle)) {
                    
    $threadResult   = array('errors' => false'url' => false'contents' => false);
                    
    $threadId   = (string) $threadInfo['handle'];
                    
    $threadResult['url']        = curl_getinfo($this->_curlHandles[$threadId],  CURLINFO_EFFECTIVE_URL);
                    
    #echo "LOADING: {$threadResult['url']}\n" ;     flush();
                    
    $threadResult['threadId']   = $this->_threadId_connect[$threadId];
                    
    $threadResult['errors']     = curl_error($this->_curlHandles[$threadId]);
                    
    $threadResult['contents']   = curl_multi_getcontent($this->_curlHandles[$threadId]);
                    
    $threadResult['curlinfo']   = $this->_curlObjects[$threadId]->getOptions();
                    
    $results[$threadId] = $threadResult;
                    
    call_user_func$this->_postProccesingFunc$threadResult);
                    
    $this->_killThread($threadId);
                    
    $this->_threadsRunning--;
                }
            }
            return 
    $results;
        }

        public 
    $_threadId_connect = array();

    // установка callback функции
        
    public function setProcessing($post) {
            
    $this->_postProccesingFunc $post;
        }
    Пример
    PHP:
       for($i=0$i<20$i++) {
     
                
    $urls['url'][]= 'http://proekt/pi_multicurl/samples/chek.php?x=index()_'.$i;
     
        }
        
    $results = array();
        try {
            
    $cURL =new Pi_MultiCurl_extOptions ( );
        } catch (
    Pi_MultiCurl_Exception $e) {
            echo 
    $e->getMessage();
            exit();
        }
        
    define('URL_STACK_SIZE',  );
        
    $cURL->setThreadsCount(URL_STACK_SIZE);
        
    $cURL->setThreadTimeout(30);
        
    $cURL->setUsleepTime(600);
    // функция обработки полученных данных
        
    function rollingPost($thread) {
            echo  
    "Это {$thread['threadId']}-й элемент \$urls['url']CONTENT: ".$thread['contents'];
            
    flush();
        }
            
    $cURL->setProcessing"rollingPost");
            
    $cURL->setUrls($urls['url']);
            
    $cURL->getUrls();
    Вроде ничего не забыл. Для обратной совместимости добавил $thread['threadId']. Он указывает на ключ элемента, взятого из массива $urls['url'].
     
  3. -=BlackSmoke=-

    -=BlackSmoke=-

    Регистр.:
    4 авг 2009
    Сообщения:
    280
    Симпатии:
    49
    Jeurey, запости, пожалуйста, пример с работой с куками :)
     
  4. -=BlackSmoke=-

    -=BlackSmoke=-

    Регистр.:
    4 авг 2009
    Сообщения:
    280
    Симпатии:
    49
    и что будет, если внутри одного потока открыть обычный курл?
     
  5. stargazerrrrr

    stargazerrrrr Fearless

    Регистр.:
    9 дек 2009
    Сообщения:
    202
    Симпатии:
    74
    Я для этих целей rolling curl юзаю. Позволяет параллельное выполнение большого количества асинхронных HTTP-запросов при помощи curl. Правильно чистит память, не простаивает зря, выполняя одновременно заданное число запросов. Обрабатывает каждый ответ сразу после выполнения запроса.
    Сайт разработчика: Перейти по ссылке
    SVN: Перейти по ссылке
    Кстати автор этого класса - известный кодер Sam Dark, один из ведущих разработчиков на основе yii фреймворка.

    Пример
    PHP:
    // an array of URL's to fetch
    $urls = array("http://www.google.com",
                  
    "http://www.facebook.com",
                  
    "http://www.yahoo.com");

    // a function that will process the returned responses
    function request_callback($response$info$request) {
        
    // parse the page title out of the returned HTML
        
    if (preg_match("~<title>(.*?)</title>~i"$response$out)) {
            
    $title $out[1];
        }
        echo 
    "<b>$title</b><br />";
        
    print_r($info);
        echo 
    "<hr>";
    }

    // create a new RollingCurl object and pass it the name of your custom callback function
    $rc = new RollingCurl("request_callback");
    // the window size determines how many simultaneous requests to allow.
    $rc->window_size 20;
    foreach (
    $urls as $url) {
        
    // add each request to the RollingCurl object
        
    $request = new RollingCurlRequest($url);
        
    $rc->add($request);
    }
    $rc->execute();
     
  6. kactetus

    kactetus митя is here )

    Регистр.:
    26 авг 2007
    Сообщения:
    441
    Симпатии:
    278
    Нормально

    stargazerrrrr класс вроде не плохой, сам пользуешся? как по стабильности?

    Отчет по работе класса.
    Сам щас тестил загнал 3138 урлов.
    Время работы 570 сек. + сохранял каждый результат (title) в txt файл.
    Скорость инета 3 мбит/с
     
  7. stargazerrrrr

    stargazerrrrr Fearless

    Регистр.:
    9 дек 2009
    Сообщения:
    202
    Симпатии:
    74
    Пользуюсь конечно. Советую поиграться с параметром window_size (кол-во параллельных потоков), чтобы забить канал под завязку.

    У меня на средненьком VPS на 100 потоках работает вот так:

    Number of proxies in list: 1372
    Checking time: 33.446796 sec.(or 0.5574466 min)
    41.0203715776 proxies a second
     
  8. kactetus

    kactetus митя is here )

    Регистр.:
    26 авг 2007
    Сообщения:
    441
    Симпатии:
    278
    Jeurey По поводу Pi_MultiCurl класс еще развивается?
    Где можно скачать данный класс а то ни сайт, ни файлов НЕТ.

    stargazerrrrr как выставить правильно timeout на соединие в потоке?
    Параметром $rc->window_size = 20; - выставляем количество потоков.

    Заглянув в код класса, посмотрел что есть переменная timeout, по аналогии выставляю $rc->timeout = 10; правильно ли?
    Просто есть парсер картинок, хочу добавить в него мультипоточность, так получается что некоторые картинки не доконца докачиваются или не могут открыться.
     
  9. KillDead

    KillDead

    Регистр.:
    11 авг 2006
    Сообщения:
    889
    Симпатии:
    556
    Хз, развивается ли, думаю только для своих проектах, т.к. много в нём нет, но и сейчас данный класс неплохо написан. Я всё хочу довести до ума свай класс и выложить, или хотя бы переделанный pi_curl, но всё руки не доходят да и постоянно баги новые появляются.
    Вот примитивные тесты, обычный гет запрос:
    1- мой класс
    2- rc
    количество запросов 100 потоков 5
    1 [time] => 4.908
    2 [time] => 4.1403
    количество запросов 100 потоков 50
    1 [time] => 3.8969
    2 [time] => 1.9794
    количество запросов 1000 потоков 50
    1 [time] => 7.5136
    2 [time] => 9.8128
    количество запросов 1000 потоков 100
    1 [time] => 4.8728
    2 [time] => 13.4546


    Вообще, настоящие тесты нужно было бы провести (включить куки, пост, выдать на каждые чётные запросы ошибку и посмотреть не затормозит ли класс), возможно позже, а сейчас:
    О скорости ничего хорошего сказать не могу- мой класс построен на Pi_MultiCurl, который довольно сильно изменён, и включает в себя ещё кучу разных проверок, генераций, переопределений, а чистая библиотека rc - ему проигрывает. Очень не хорошо. Хотя в принципе, порой не так критично- ну будет парсится сайт не час, а полтора часа.
    О памяти - вот в чём скорее всего выигрывает rc. Если в мой класс загрузить очень много ссылок кучей- память будет утекать. Но пока не нужно обрабатывать 500к ссылок за раз- можно не парится.
     
  10. kactetus

    kactetus митя is here )

    Регистр.:
    26 авг 2007
    Сообщения:
    441
    Симпатии:
    278
    KillDead а можеш поделится, хотел бы глянуть на него :), если не жалко. Хотелось бы глянуть как постороен данный класс.
     
Статус темы:
Закрыта.