PHP CURL справка по многопоточным и однопоточным функциям. Как мне это сделать? - PullRequest
1 голос
/ 09 января 2020

Я нашел здесь функцию: http://archevery.blogspot.com/2013/07/php-curl-multi-threading.html

Я использую ее для отправки массива URL-адресов для запуска и обработки как можно быстрее с помощью многопоточных запросов curl. Это прекрасно работает.

НЕКОТОРЫЕ из URL, которые я хочу отправить, требуют, чтобы они обрабатывались по порядку, не в одно и то же время, а в последовательности.

Как этого добиться?

Пример:

URL-A URL-B URL-B- C -> Все функции отключаются одновременно

URL-D URL-E -> Должен дождитесь окончания действия URL-D sh, прежде чем URL-E сработает.

Моя цель - создать систему управления задачами, которая позволит мне добавлять PHP приложений в качестве «Задач» в базу данных. У меня есть отношение заголовка / детализации с задачами, поэтому задача с одним заголовком и одной деталью может быть отослана многопоточно, но задача с одним заголовком и несколькими деталями должна отсылаться в порядке задач детализации.

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

Я попробовал эту функцию, чтобы отправить несколько задач до, но он ждет, пока каждая задача завершится sh, прежде чем перейти к следующей. Мне нужно как-то объединить многопоточную функцию из приведенного выше URL с этой. Вот моя многопоточная функция скручивания:

function runRequests($url_array, $thread_width = 10) {
    $threads = 0;
    $master = curl_multi_init();
    $curl_opts = array(CURLOPT_RETURNTRANSFER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS => 5,
        CURLOPT_CONNECTTIMEOUT => 15,
        CURLOPT_TIMEOUT => 15,
        CURLOPT_RETURNTRANSFER => TRUE);
    $results = array();
    $count = 0;
    foreach($url_array as $url) {
        $ch = curl_init();
        $curl_opts = [CURLOPT_URL => $url];
        curl_setopt_array($ch, $curl_opts);
        curl_multi_add_handle($master, $ch); //push URL for single rec send into curl stack
        $results[$count] = array("url" => $url, "handle" => $ch);
        $threads++;
        $count++;
        if($threads >= $thread_width) { //start running when stack is full to width
            while($threads >= $thread_width) {
                //usleep(100);
                while(($execrun = curl_multi_exec($master, $running)) === -1){}
                curl_multi_select($master);
                // a request was just completed - find out which one and remove it from stack
                while($done = curl_multi_info_read($master)) {
                    foreach($results as &$res) {
                        if($res['handle'] == $done['handle']) {
                            $res['result'] = curl_multi_getcontent($done['handle']);
                        }
                    }
                    curl_multi_remove_handle($master, $done['handle']);
                    curl_close($done['handle']);
                    $threads--;
                }
            }
        }
    }
    do { //finish sending remaining queue items when all have been added to curl
        //usleep(100);
        while(($execrun = curl_multi_exec($master, $running)) === -1){}
        curl_multi_select($master);
        while($done = curl_multi_info_read($master)) {
            foreach($results as &$res) {
                if($res['handle'] == $done['handle']) {
                    $res['result'] = curl_multi_getcontent($done['handle']);
                }
            }
            curl_multi_remove_handle($master, $done['handle']);
            curl_close($done['handle']);
            $threads--;
        }
    } while($running > 0);
    curl_multi_close($master);
    return $results;
}

, а вот однопоточная функция скручивания.

function runSingleRequests($url_array) {
foreach($url_array as $url) {   

// Initialize a CURL session. 
$ch = curl_init();  

// Page contents not needed. 
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 0); 

// grab URL and pass it to the variable. 
curl_setopt($ch, CURLOPT_URL, $url); 

// process the request.  
$result = curl_exec($ch);

    }

Оба принимают массив URL в качестве входных данных.

В настоящее время у меня есть массив всех отдельных задач и еще один массив всех нескольких задач с «идентификатором заголовка», который позволяет мне узнать, к какой задаче заголовка относится каждая задача детализации.

Любая помощь по теории или коду будет наиболее ценится. Спасибо!

Ответы [ 2 ]

1 голос
/ 09 января 2020

Почему бы вам не использовать рудиментарный планировщик задач для планирования ваших запросов и последующих действий вместо того, чтобы запускать все сразу?

Посмотрите это в действии: https://ideone.com/suTUBS

<?php
class Task 
{
    protected $follow_up = [];
    protected $task_callback;

    public function __construct($task_callback) 
    {
        $this->task_callback = $task_callback;
    }

    public function addFollowUp(Task $follow_up) 
    {
        $this->follow_up[] = $follow_up;
    }

    public function complete() 
    {
        foreach($this->follow_up as $runnable) {
            $runnable->run();
        }
    }

    public function run() 
    {
        $callback = $this->task_callback;

        $callback($this);
    }
}



$provided_task_scheduler_from_somewhere = function() 
{
    $tasks = [];

    $global_message_thing = 'failed';

    $second_global_message_thing = 'failed';

    $task1 = new Task(function (Task $runner) 
    {
        $something_in_closure = function() use ($runner) {
            echo "running task one\n";
            $runner->complete();
        };
        $something_in_closure();
    });

    /**
     * use $global_message_thing as reference so we can manipulate it
     * This will make sure that the follow up on this one knows the status of what happened here
     */
    $second_follow_up = new Task(function(Task $runner) use (&$global_message_thing)
    { 
        echo "second follow up on task one.\n";
        $global_message_thing = "success";
        $runner->complete();
    });

    /**
     * Just doing things in random order to show that order doesn't really matter with a task scheduler
     * just the follow ups
     */
    $tasks[] = $task1;

    $tasks[] = new Task(function(Task $runner) 
    {
        echo "running task 2\n";
        $runner->complete();
    });

    $task1->addFollowUp(new Task(function(Task $runner) 
    { 
        echo "follow up on task one.\n";
        $runner->complete();
    }));

    $task1->addFollowUp($second_follow_up);

    /**
     * Adding the references to our "status" trackers here to know what to print
     * One will still be on failed because we did nothing with it. this way we know it works properly
     * as a control.
     */
    $second_follow_up->addFollowUp(new Task(function(Task $runner) use (&$global_message_thing, &$second_global_message_thing) {
        if($global_message_thing === "success") {
            echo "follow up on the second follow up, three layers now, w00007!\n";
        }
        if($second_global_message_thing === "success") {
            echo "you don't see this\n";
        }
        $runner->complete();
    }));
    return $tasks;
};
/**
 * Normally you'd use some aggretating function to build up your tasks
 * list or a collection of classes. I simulated that here with this callback function.
 */
$tasks = $provided_task_scheduler_from_somewhere();

foreach($tasks as $task) {
    $task->run();
}

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

In В моем примере сам объект Task передается исполняющей функции, поэтому исполняющая функция может вызывать complete, когда она завершит свою работу.
После завершения вызывается Task, определите, запланированы ли последующие задачи для выполнения и, если да, те автоматически вызываются и работают по цепочке вот так.

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

0 голосов
/ 09 января 2020

Вот более простой пример: From: http://arguments.callee.info/2010/02/21/multiple-curl-requests-with-php/

curl_multi_init. Это семейство функций позволяет комбинировать дескрипторы cURL и выполнять их одновременно.

ПРИМЕР

создавать отдельные запросы, но не выполнять их

$ch_1 = curl_init('http://webservice.one.com/');
$ch_2 = curl_init('http://webservice.two.com/');
curl_setopt($ch_1, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch_2, CURLOPT_RETURNTRANSFER, true);

построить дескриптор мульти-скручивания, добавив $ ch

$mh = curl_multi_init();
curl_multi_add_handle($mh, $ch_1);
curl_multi_add_handle($mh, $ch_2);

, выполнить все запросы одновременно и продолжить, когда все будут завершены

  $running = null;
  do {
    curl_multi_exec($mh, $running);
  } while ($running);

закрыть дескрипторы

curl_multi_remove_handle($mh, $ch1);
curl_multi_remove_handle($mh, $ch2);
curl_multi_close($mh);

все наши запросы выполнены, теперь мы можем получить доступ к результатам

$response_1 = curl_multi_getcontent($ch_1);
$response_2 = curl_multi_getcontent($ch_2);
echo "$response_1 $response_2"; // output results

Если для возврата на оба сайта требуется одна секунда, мы буквально сократили время загрузки нашей страницы наполовину, используя второй пример вместо первого!

Ссылки: https://www.php.net/manual/en/function.curl-multi-init.php

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