«Многопоточность» в PHP (curl)
Использование библиотеки curl.
Эта статья является первой из серии “Многопоточность” в PHP
Curl – это библиотека, позволяющая подсоединяться к разным серверам по разным протоколам. Обладает удобством в работе и способностью гибко настраиваться.
Curl реализует механизм множественных запросов, или мультизапросов. Его принцип заключается в том, что посылается несколько запросов, при этом перед отправкой следующего не ожидается ответ на предыдущий.
Используем это в нашем примере скачивания нескольких страниц.
Рассмотрим сначала процесс скачивания содержимого с одного url.
<?php $url = 'mail.ru'; // инициализация сеанса curl $ch = curl_init('http://'.$url); // curl_exec будет возвращать результат curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // не будет возвращаться http-заголовок curl_setopt($ch, CURLOPT_HEADER, 0); // загрузка страницы и выдача её браузеру $content = curl_exec($ch); // завершение сеанса и освобождение ресурсов curl_close($ch); ?>
Здесь функцией curl_init мы инициализируем сеанс curl и в качестве параметра передаем урл страницы, которую хотим скачать. Далее первым вызовом функции curl_setopt говорим, что результат надо вернуть, а не вывести в браузер, и вторым запрещаем передачу нам http-ответа сервера. curl_setopt принимаюет в качестве параметров дескриптор соединения $ch, название опции и ее значение соответственно. С помощью curl_setopt можно задать много параметров для более тонкого управления соединением, подробнее читайте в мануале к этой библиотеке. Затем функцией curl_exec производим собственно скачивание и в завершении закрываем соединение – curl_close. В переменной $content у нас теперь находится код страницы, указанной в $url.
А сейчас давайте попробуем скачать сразу несколько страниц (основа примера взята из документации на php.net):
<?php // страницы, содержимое которых надо получить $urls = array('yandex.ru', 'google.ru', 'mail.ru', 'rambler.ru'); // инициализируем "контейнер" для отдельных соединений (мультикурл) $cmh = curl_multi_init(); // массив заданий для мультикурла $tasks = array(); // перебираем наши урлы foreach ($urls as $url) { // инициализируем отдельное соединение (поток) $ch = curl_init('http://'.$url); // если будет редирект - перейти по нему curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); // возвращать результат curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); // не возвращать http-заголовок curl_setopt($ch, CURLOPT_HEADER, 0); // таймаут соединения curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); // таймаут ожидания curl_setopt($ch, CURLOPT_TIMEOUT, 10); // добавляем дескриптор потока в массив заданий $tasks[$url] = $ch; // добавляем дескриптор потока в мультикурл curl_multi_add_handle($cmh, $ch); } // количество активных потоков $active = null; // запускаем выполнение потоков do { $mrc = curl_multi_exec($cmh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); // выполняем, пока есть активные потоки while ($active && ($mrc == CURLM_OK)) { // если какой-либо поток готов к действиям if (curl_multi_select($cmh) != -1) { // ждем, пока что-нибудь изменится do { $mrc = curl_multi_exec($cmh, $active); // получаем информацию о потоке $info = curl_multi_info_read($cmh); // если поток завершился if ($info['msg'] == CURLMSG_DONE) { $ch = $info['handle']; // ищем урл страницы по дескриптору потока в массиве заданий $url = array_search($ch, $tasks); // забираем содержимое $tasks[$url] = curl_multi_getcontent($ch); // удаляем поток из мультикурла curl_multi_remove_handle($cmh, $ch); // закрываем отдельное соединение (поток) curl_close($ch); } } while ($mrc == CURLM_CALL_MULTI_PERFORM); } } // закрываем мультикурл curl_multi_close($cmh); ?>
Код подробно откомментирован, но давайте разберем все по порядку.
В 7 строчке мы инициализируем контейнер для отдельных соединений curl (далее я буду называть его мультикурл), именно он позволит нам проводить операции с ними параллельно.
Далее в цикле инициализируем соединение (назовем его поток) для каждого урла из нашего массива, попутно добавляя его в мультикурл и в массив заданий $tasks. $tasks – массив, в котором ключами являются адреса наших страниц, а значениями – соответствующие дескрипторы curl. Функция curl_multi_add_handle добавляет к дескриптору нашего мультизапросного соединения отдельное созданное соединение.
В 34 строке запускается цикл для начала работы нашего мультикурла. Функция curl_multi_exec одновременно отправляет на выполнение все объявленные потоки, при этом в переменную $active заносится количество выполняемых потоков.
В основном цикле, начинающемся на 40 строчке, происходят главные действия. Он выполняется до тех пор, пока есть незавершенные потоки или пока не произошла ошибка. В 42 строке вызывается функция curl_multi_select, которая проверяет готовность какого-либо из потоков к дальнейшим действиям с ним. Затем, в 47 строке, функцией curl_multi_info_read получаем информацию о потоке. Но так как curl_multi_info_read обновляет возвращаемую информацию только после вызова curl_multi_exec, сделаем это в строке 45.
Функция curl_multi_info_read возвращает массив, в котором нас интересуют ключи ‘msg’ и ‘handle’. По ‘msg’ мы проверяем, выполнился ли поток, а по ‘handle’ узнаем его дескриптор. Получив дескриптор, ищем по массиву заданий, к какому урлу он относится и записываем вместо него вожделенное содержимое страницы, получаемое функцией curl_multi_getcontent.
Теперь, когда данный поток выполнил свою задачу, удаляем его из мультикурла, а потом закрываем самого.
После завершения всех потоков функцией curl_multi_close закрываем мультикурл.
Сейчас в массиве заданий $tasks находятся html-коды заданных страниц.
Надеюсь, после прочтения статьи стало немного понятнее, как можно реализовать выполнение “многопоточных” запросов на PHP.
В следующей статье рассмотрим использование stream-функций для подобных целей.
Источник: “Многопоточность” в PHP (curl) / Статьи / Работа для программистов
Рекомендую для прочтения: Уроки PHP. Урок № 11 — CURL
18 февраля, 2014 в 00:23|date at time
Код 2 нерабочий он зависает на ожидании потоков бред.
24 апреля, 2014 в 12:36|date at time
Незнаю что у вас висит
у меня всё отлично работает
просто скопировал и вставил, заменил только ссылки, у меня их около 100 штук
2 апреля, 2015 в 10:48|date at time
У меня тоже: пока работет мультиКурл — не отвечают другие страницы сайта. Как только он завершит свобю работу — все начинает опять нормально грузится!
25 сентября, 2020 в 23:34|date at time
2020 гНе работает если использовать https в массиве ссылок. Нужно использовать другую технологию, стучите в телегу: @antb0