«Многопоточность» в 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