многопоточный сканер в PHP

Привет всем еще раз!

Нам нужна помощь, чтобы разработать и внедрить в наш сканер функциональность multi-curl. У нас огромный массивссылки для сканирования " и мы закидываем их с помощью Foreach.

Позволять's использовать псевдокод, чтобы понять логику:

    1) While ($links_to_be_scanned > 0).
    2) Foreach ($links_to_be_scanned as $link_to_be_scanned).
    3) Scan_the_link() and run some other functions.
    4) Extract the new links from the xdom.
    5) Push the new links into $links_to_be_scanned.
    5) Push the current link into $links_already_scanned.
    6) Remove the current link from $links_to_be_scanned.

Теперь нам нужно определить максимальное количество параллельных соединений и иметь возможность запускать этот процесс для каждой ссылки параллельно.

Я понимаю что мынам нужно будет создать $ links_being_scanned или какую-то очередь.

Я действительно не уверен, как подойти к этой проблеме, если честно,если бы кто-нибудь мог предоставить какой-нибудь фрагмент или идею для его решения, это было бы очень признательно.

Заранее спасибо! Крис;

Extended:

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

Даже после того, как muticurl, я в конечном итоге должен был бы найти способ выполнить все эти операции параллельно. Весь алгоритм, описанный ниже, должен работать параллельно.

Итак, теперь переосмысливая, мы должны были бы сделать что-то вроде этого:

  While (There's links to be scanned)
  Foreach ($Link_to_scann as $link)
  If (There's less than 10 scanners running)
  Launch_a_new_scanner($link)
  Remove the link from $links_to_be_scanned array
  Push the link into $links_on_queue array
  Endif;

И каждый сканер делает (это должно выполняться параллельно):

  Create an object with the given link
  Send a curl request to the given link
  Create a dom and an Xdom with the response body
  Perform other operations over the response body
  Remove the link from the $links_on_queue array
  Push the link into the $links_already_scanned array

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

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

Я предполагаю, что мне нужно подойти к этому с помощью fsockopen или pcntl_fork.

Предложения, комментарии, частичные решения и даже "удачи" будет более чем оценено!

Большое спасибо!

Ответы на вопрос(4)

Решение Вопроса

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Этот ответ связывает проект с открытым исходным кодом, с которым ям участвует. Там. Вы'я был предупрежден.

HTTP-клиент Artax является HTTP-библиотекой на основе сокетов, которая (помимо прочего) предлагает настраиваемый контроль над количеством одновременных соединений с открытым сокетом с отдельными хостами при выполнении нескольких асинхронных HTTP-запросов.

Ограничение количества одновременных соединений легко выполняется. Рассматривать:

<!--?php

use Artax\Client, Artax\Response;

require dirname(__DIR__) . '/autoload.php';

$client = new Client;

// Defaults to max of 8 concurrent connections per host
$client--->setOption('maxConnectionsPerHost', 2);

$requests = array(
    'so-home'    => 'http://stackoverflow.com',
    'so-php'     => 'http://stackoverflow.com/questions/tagged/php',
    'so-python'  => 'http://stackoverflow.com/questions/tagged/python',
    'so-http'    => 'http://stackoverflow.com/questions/tagged/http',
    'so-html'    => 'http://stackoverflow.com/questions/tagged/html',
    'so-css'     => 'http://stackoverflow.com/questions/tagged/css',
    'so-js'      => 'http://stackoverflow.com/questions/tagged/javascript'
);

$onResponse = function($requestKey, Response $r) {
    echo $requestKey, ' :: ', $r->getStatus();
};

$onError = function($requestKey, Exception $e) {
    echo $requestKey, ' :: ', $e->getMessage();
}

$client->requestMulti($requests, $onResponse, $onError);

ВАЖНЫЙ: В приведенном выше примереClient::requestMulti метод делает все указанные запросыасинхронно, Поскольку ограничение параллелизма для каждого хоста установлено в2клиент откроет новые соединения для первых двух запросов и затем повторно использует те же сокеты для других запросов, ставя в очередь запросы до тех пор, пока один из двух сокетов не станет доступным.

 uınbɐɥs30 окт. 2012 г., 19:10
+1 - выглядит намного проще, чем cURL.
 Chris Russo07 нояб. 2012 г., 06:32
Ну, это выглядит потрясающе и как правильное решение. Мы'с нетерпением ждем его реализации. Заранее спасибо!
 user112539430 окт. 2012 г., 19:10
+1 понадобится еще много строк с php-curl, но покаЯ еще не проверил вашу библиотеку

но вы должны понять

$request_pool = array();

function CreateHandle($url) {
    $handle = curl_init($url);

    // set curl options here

    return $handle;
}

function Process($data) {
    global $request_pool;

    // do something with data

    array_push($request_pool , CreateHandle($some_new_url));
}

function RunMulti() {
    global $request_pool;

    $multi_handle = curl_multi_init();

    $active_request_pool = array();

    $running = 0;
    $active_request_count = 0;
    $active_request_max = 10; // adjust as necessary
    do {
        $waiting_request_count = count($request_pool);
        while(($active_request_count < $active_request_max) && ($waiting_request_count > 0)) {
            $request = array_shift($request_pool);
            curl_multi_add_handle($multi_handle , $request);
            $active_request_pool[(int)$request] = $request;

            $waiting_request_count--;
            $active_request_count++;
        }

        curl_multi_exec($multi_handle , $running);
        curl_multi_select($multi_handle);
        while($info = curl_multi_info_read($multi_handle)) {
            $curl_handle = $info['handle'];
            call_user_func('Process' , curl_multi_getcontent($curl_handle));
            curl_multi_remove_handle($multi_handle , $curl_handle);
            curl_close($curl_handle);
            $active_request_count--;
        }

    } while($active_request_count > 0 || $waiting_request_count > 0);

    curl_multi_close($multi_handle);
}
 Chris Russo30 окт. 2012 г., 14:37
Хорошо, есть какая-то ссылка или более подробная версия этого подхода? похоже, это может быть полезно. Я только что понял, что сложная часть - это не сам мультискрутка, а количество операций, выполненных с каждой ссылкой после параллельного запроса. Даже после того, как muticurl, я в конечном итоге должен был бы найти способ выполнить все эти операции параллельно. Весь алгоритм, описанный ниже, должен работать параллельно. Я'уточню вопрос. Большое спасибо @ninaj
 ninaj30 окт. 2012 г., 14:48
Некоторое время назад я нашел какой-то пример в Интернете, а затем он превратился в нечто вроде этого в моей базе кода, так что я действительно нездесь нет никакой ссылки, если обработка данных является медленной частью этогопредложил бы разбить его на части и использовать что-то вроде Gearman (gearman.org/index.php?id=gearman_php_extension) справляться с рабочими нагрузками, так как из моего опыта пытаюсьнить" PHP это боль.
 Chris Russo30 окт. 2012 г., 14:53
Принимая во внимание, что даже в случае реализации нескольких скручиваний остальные процессы и функции останутся однопоточными, мне в конечном итоге придется как-то разветвляться и запускать каждый алгоритм параллельно. Я могу'найти другой вариант, кроме использования fsockopen или pcntl_fork ...

который я использовал для сбора адресов электронной почты с определенного сайта. Вы можете изменить его в соответствии с вашими потребностями. Были некоторые проблемы с относительным URL 'там. И я не использую CURL здесь.

<!--?php
error_reporting(E_ALL);
$home   = 'http://kharkov-reklama.com.ua/jborudovanie/';
$writer = new RWriter('C:\parser_13-09-2012_05.txt');
set_time_limit(0);
ini_set('memory_limit', '512M');

function scan_page($home, $full_url, &$writer) {

    static $done = array();
    $done[] = $full_url;

    // Scan only internal links. Do not scan all the internet!))
    if (strpos($full_url, $home) === false) {
        return false;
    }
    $html = @file_get_contents($full_url);
    if (empty($html) || (strpos($html, '<body') === false && strpos($html, '<BODY') === false)) {
        return false;
    }

    echo $full_url . '<br /-->';

    preg_match_all('/([A-Za-z0-9_\-]+\.)*[A-Za-z0-9_\-][email protected]([A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9]\.)+[A-Za-z]{2,4}/', $html, $emails);

    if (!empty($emails) && is_array($emails)) {
        foreach ($emails as $email_group) {
            if (is_array($email_group)) {
                foreach ($email_group as $email) {
                    if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
                        $writer->write($email);
                    }
                }
            }
        }
    }

    $regexp = "<a\s[^>]*href=(\"??)([^\" >]*?)\\1[^>]*>(.*)<\/a>";
    preg_match_all("/$regexp/siU", $html, $matches, PREG_SET_ORDER);
    if (is_array($matches)) {
        foreach($matches as $match) {
            if (!empty($match[2]) && is_scalar($match[2])) {
                $url = $match[2];
                if (!filter_var($url, FILTER_VALIDATE_URL)) {
                    $url = $home . $url;
                }
                if (!in_array($url, $done)) {
                    scan_page($home, $url, $writer);
                }
            }
        }
    }
}

class RWriter {
    private $_fh = null;

    private $_written = array();

    public function __construct($fname) {
        $this->_fh = fopen($fname, 'w+');
    }

    public function write($line) {
        if (in_array($line, $this->_written)) {
            return;
        }
        $this->_written[] = $line;
        echo $line . '<br>';
        fwrite($this->_fh, "{$line}\r\n");
    }

    public function __destruct() {
        fclose($this->_fh);
    }
}

scan_page($home, 'http://kharkov-reklama.com.ua/jborudovanie/', $writer);
</a\s[^>
 Bogdan Burim30 окт. 2012 г., 14:27
Это не параллельно. Это просто рекурсивно.
 Chris Russo30 окт. 2012 г., 14:25
Большое спасибо Богдану, но насколько явидел код ниже нене справиться с параллельной обработкой, не так ли?
 Chris Russo30 окт. 2012 г., 14:33
Мы'сейчас вы находитесь в том же положении, большое спасибо! :)

RabbitMQ это очень хорошее решение, которое я использовал. Существует такжеGearman но я думаю, что это твой выбор.

Я предпочитаю RabbitMQ.

Ваш ответ на вопрос