Параллельная загрузка страниц с использованием PHP - PullRequest
7 голосов
/ 10 февраля 2012

Мне нужно отказаться от веб-сайта, на котором мне нужно получить несколько URL-адресов, а затем обработать их один за другим.Текущий процесс выглядит примерно так:

Я выбираю базовый URL и получаю все вторичные URL с этой страницы, затем для каждого вторичного URL я выбираю этот URL, обрабатываю найденную страницу, загружаю несколько фотографий (что занимает довольно много времени).долгое время) и сохраните эти данные в базе данных, затем получите следующий URL и повторите процесс.

В этом процессе, я думаю, я трачу некоторое время на выборку вторичного URL в начале каждой итерации.Поэтому я пытаюсь получить следующие URL-адреса параллельно при обработке первой итерации.

Решение, на мой взгляд, заключается в том, чтобы из основного процесса вызвать скрипт PHP, скажем, загрузчик, который загрузит все URL-адреса (с curl_multiили wget) и сохраните их в некоторой базе данных.

Мои вопросы

  • Как вызвать такой загрузчик асинхронно , я не хочу, чтобы мой основной скрипт ждал, пока загрузчик завершит работу.
  • Любое место для хранения загруженных данных, например общая память.Конечно, кроме базы данных.
  • Есть ли вероятность того, что данные будут повреждены при хранении и получении, как избежать этого?
  • Также, пожалуйста, сообщите мне, если у кого-то есть лучший план.

Ответы [ 5 ]

6 голосов
/ 11 февраля 2012

Когда я слышу, что кто-то использует curl_multi_exec, обычно получается, что он просто загружает его, скажем, 100 URL-адресами, затем ждет, когда все будет завершено, и затем обрабатывает их все, а затем начинает заново со следующими 100 URL-адресами ... Вини Я тоже так делал, но потом обнаружил, что можно удалить / добавить маркеры в curl_multi, пока что-то еще происходит, и это действительно экономит много времени, особенно если вы повторно используете уже открытые соединения. Я написал небольшую библиотеку для обработки очереди запросов с обратными вызовами; Конечно, я не публикую здесь полную версию ("small" - это все еще немного кода), но вот упрощенная версия, чтобы дать вам общее представление:

public function launch() {
    $channels = $freeChannels = array_fill(0, $this->maxConnections, NULL);
    $activeJobs = array();
    $running = 0;
    do {
        // pick jobs for free channels:
        while ( !(empty($freeChannels) || empty($this->jobQueue)) ) {
            // take free channel, (re)init curl handle and let
            // queued object set options
            $chId = key($freeChannels);
            if (empty($channels[$chId])) {
                $channels[$chId] = curl_init();
            }
            $job = array_pop($this->jobQueue);
            $job->init($channels[$chId]);
            curl_multi_add_handle($this->master, $channels[$chId]);
            $activeJobs[$chId] = $job;
            unset($freeChannels[$chId]);
        }
        $pending = count($activeJobs);

        // launch them:
        if ($pending > 0) {
            while(($mrc = curl_multi_exec($this->master, $running)) == CURLM_CALL_MULTI_PERFORM);
                // poke it while it wants
            curl_multi_select($this->master);
                // wait for some activity, don't eat CPU
            while ($running < $pending && ($info = curl_multi_info_read($this->master))) {
                // some connection(s) finished, locate that job and run response handler:
                $pending--;
                $chId = array_search($info['handle'], $channels);
                $content = curl_multi_getcontent($channels[$chId]);
                curl_multi_remove_handle($this->master, $channels[$chId]);
                $freeChannels[$chId] = NULL;
                    // free up this channel
                if ( !array_key_exists($chId, $activeJobs) ) {
                    // impossible, but...
                    continue;
                }
                $activeJobs[$chId]->onComplete($content);
                unset($activeJobs[$chId]);
            }
        }
    } while ( ($running > 0 && $mrc == CURLM_OK) || !empty($this->jobQueue) );
}

В моей версии $ jobs на самом деле представляют собой отдельный класс, а не экземпляры контроллеров или моделей. Они просто обрабатывают настройку параметров cURL, анализируют ответ и вызывают заданный обратный вызов onComplete. С этой структурой новые запросы начнутся, как только что-то из пула завершится.

Конечно, это на самом деле не спасет вас, если не просто получение требует времени, но и обработки ... И это не настоящая параллельная обработка. Но я все еще надеюсь, что это поможет. :)

P.S. сделал трюк для меня. :) Однажды 8-часовая работа теперь выполняется за 3-4 минуты, используя пул из 50 соединений. Не могу описать это чувство. :) Я не ожидал, что он будет работать так, как планировалось, потому что с PHP он редко работает точно так, как предполагалось ... Это было похоже на "хорошо, надеюсь, что оно завершится как минимум через час ... Wha ... Подождите ... . Уже ?! 8-О "

2 голосов
/ 11 февраля 2012

Помимо решения curl multi, у другого просто есть партия рабочих-механиков . Если вы пойдете этим путем, я найду supervisord хороший способ начать загрузку рабочих-демонов.

2 голосов
/ 10 февраля 2012

Вы можете использовать curl_multi: http://www.somacon.com/p537.php

Вы также можете рассмотреть возможность использования этой клиентской стороны и использования Javascript.


Другое решение - написать искатель / собиратель, которому вы отправляете массив URL-адресов, затем он выполняет параллельную работу и возвращает массив JSON после его завершения.

Другими словами: если у вас есть 100 URL-адресов, вы можете поместить этот массив (возможно, также как JSON) в mysite.tld / huntergatherer - он делает все, что захочет, на любом языке, который вы хотите, и просто возвращает JSON.

1 голос
/ 12 февраля 2012

Вещи, на которые следует обратить внимание в дополнение к CURL multi:

  • Неблокирующие потоки (пример: PHP-MIO )
  • ZeroMQ для порождения многих рабочих, выполняющих запросы асинхронно

В то время как node.js, ruby ​​EventMachine или аналогичные инструменты довольно хороши для выполнения этих задач, вещи, о которых я упоминал, также облегчают работу в PHP.

0 голосов
/ 10 февраля 2012

Попробуйте выполнить из PHP, скрипты python-pycurl. Проще, быстрее, чем PHP curl.

...