Комментарии 47
красиво реализовано, взял на заметку, спасибо!
0
if (count($this->sockets) == ) {
тут закралась опечатка или это новая магия? :)
+3
А CURL не подходит для таких задач?
+4
Ну curl не подходит если надо постоянно держать некоторое число запросов запущенными. То есть пока не выполниться вся группа запросов, нельзя запустить ещё один, ну или приодеться использовать костыли.
0
Вот это пробовали curl_multi_init (http://us3.php.net/manual/en/function.curl-multi-init.php)?
+2
Я знаю как работает curl_multi_init. Как на нем реализовать подобный код?
for ($i=0;$i<5;$i++) {
$async->get('http://example.net/cluster.php');
}
while (($threads = $async->iteration()) !== false) {
foreach ($threads as $id) {
$async->get('http://example.net/cluster.php');
}
}
То есть сначала запускаем 5 потоков, как только завершается один из них запускаем следующий, таким образом все время будет запущенно 5 потоков. Я не видел подобных примеров на curl, там запросы запускаются группами то есть сначала 5 запросов, потом ещё 5 запросов и т. д.
for ($i=0;$i<5;$i++) {
$async->get('http://example.net/cluster.php');
}
while (($threads = $async->iteration()) !== false) {
foreach ($threads as $id) {
$async->get('http://example.net/cluster.php');
}
}
То есть сначала запускаем 5 потоков, как только завершается один из них запускаем следующий, таким образом все время будет запущенно 5 потоков. Я не видел подобных примеров на curl, там запросы запускаются группами то есть сначала 5 запросов, потом ещё 5 запросов и т. д.
-2
Вы определённо не правы.
Как раз таки это CURL умеет, я как-то раз реализовывал подобную штуку.
См. в xpart.ru/resume/sources.zip исходник «Search Engines Multithread Parser\classes\class.multipager.php»
Как раз таки это CURL умеет, я как-то раз реализовывал подобную штуку.
См. в xpart.ru/resume/sources.zip исходник «Search Engines Multithread Parser\classes\class.multipager.php»
+4
мультикурл нестабилен и жрет много ресурсов.
+1
Лично я ничего такого не наблюдал. Можете подробнее рассказать?
0
Ну есть там засада с потреблением памяти. Я так понимаю, интерфейс построен так, что приходится выделить большой блок памяти заранее. Обходится легко. Вот в библиотеке code.google.com/p/multicurl-library/ даже в примере это сделано.
0
Cсылка на либу MultiCURL вот: code.google.com/p/multicurl-library/downloads/list
Вот небольшое описание на русском языке, с примером: www.weblancer.net/users/tvv/portfolio/231798.html
Примечание к описанию: wait() вызывается в самом конце, скачивание начинается сразу при первом же addUrl(). Cкачивание c каждого ресурса происходит абсолютно независимо друг от других ресурсов, для каждой скачки можно истанавливать свои параметры (например, разные прокси, разные параметры авторизации, разные протоколы, включая HTTPS, разные таймауты и т.д.). Параллельно скачиванию можно выполнять любые другие действия.
Про память — таки да, неконтролируемо жрет-с. Это недостаток этого решения. Как побороть пока не придумал. Хотя для большинства проектов, где данная либа используется, это не критично.
Вот небольшое описание на русском языке, с примером: www.weblancer.net/users/tvv/portfolio/231798.html
Примечание к описанию: wait() вызывается в самом конце, скачивание начинается сразу при первом же addUrl(). Cкачивание c каждого ресурса происходит абсолютно независимо друг от других ресурсов, для каждой скачки можно истанавливать свои параметры (например, разные прокси, разные параметры авторизации, разные протоколы, включая HTTPS, разные таймауты и т.д.). Параллельно скачиванию можно выполнять любые другие действия.
Про память — таки да, неконтролируемо жрет-с. Это недостаток этого решения. Как побороть пока не придумал. Хотя для большинства проектов, где данная либа используется, это не критично.
0
> мультикурл нестабилен и жрет много ресурсов.
«нестабилен» — это не так, весьма стабилен (но в некоторых старых версиях бывают утечки памяти в некоторых случаях! так что всегда старайтесь использовать последние версии)
«жрет много ресурсов» — да, согласен, что есть то есть. это плата за универсальность и за следование стандартам, и это стоит того. хотя в любом случае, слово «много» — это понятие относительное, и не думаю что подобное решение в лоб, реализованное на PHP (с поддержкой разных протоколов, проксей, кукисов, таймаутов, редиректов и т.д.), будет потреблять меньше ресурсов.
«нестабилен» — это не так, весьма стабилен (но в некоторых старых версиях бывают утечки памяти в некоторых случаях! так что всегда старайтесь использовать последние версии)
«жрет много ресурсов» — да, согласен, что есть то есть. это плата за универсальность и за следование стандартам, и это стоит того. хотя в любом случае, слово «много» — это понятие относительное, и не думаю что подобное решение в лоб, реализованное на PHP (с поддержкой разных протоколов, проксей, кукисов, таймаутов, редиректов и т.д.), будет потреблять меньше ресурсов.
0
вообще можно, вот костыли начнутся с получением кода ошибки и ее текста bugs.php.net/bug.php?id=48304
0
Кстати да, чем не CURL?
0
С этими сокетами все хорошо до одного момента, пока не наткнетесь на проблему «получать статус сокета»:
например, сервер вдруг затупил и не выдает ответа, в буфере ничего нету, но вы циклически его читаете и думаете что все вычитали, хотя надо просто ждать, так как данные будут чуть позже.
И что самое обидное, get_socket_status (которая могла бы помочь) с сокетами socket_create() работать не будет (точнее она не скажет eof для неблокируемого сокета)! Ей подавай fsockopen-сокеты только.
Так что ваша реализация хороша, пока сервера отвечают быстро.
например, сервер вдруг затупил и не выдает ответа, в буфере ничего нету, но вы циклически его читаете и думаете что все вычитали, хотя надо просто ждать, так как данные будут чуть позже.
И что самое обидное, get_socket_status (которая могла бы помочь) с сокетами socket_create() работать не будет (точнее она не скажет eof для неблокируемого сокета)! Ей подавай fsockopen-сокеты только.
Так что ваша реализация хороша, пока сервера отвечают быстро.
+2
Мне нужно было достучаться для своего же сервера просто на другие IP, так что в моем случае этого достаточно, но ничто не мешает добавить для каждого сокета время запуска и проверять в цикле его на таймаут.
0
Просто надо писать с использованием stream_select().
+1
Xexe :) Когда автор столкнется с нестабильными и периодически неработающими серверами — начет рварь волосы на голове, но надеюсь доведер реалиализацию до ума. Ни в коем случае не хочу обидеть — просто когда-то давно, еще во времена пхп4 стояла задача написания некоторого бота для обработки 200-300к доменов в сутки. Сами понимаете — никто не мог гарантировать работоспособность хотябы 10% из них, поэтому начались нескольконочные танцы с бубнами вокруг таймаутов (которых у автора вообще не наблюдается) и обманыванием пхп — через локальное обращение к самому себе чтобы запустить очередной пхп процесс (т.е. тупо 5-10 процессов обслуживали стабильно с жесткими таймаутами 300-400 соединений единовременно)
Автору предлагаю остановится тогда когда получится стабильно забивать свой канал под 99%, с минимальными затыками (3-5 сек в минуту) аааа себе предлагаю порытся в старых исходниках и написать либу под ZF заодно портировав под пхп5, со стрим_селект работать вроде поудобнее будет
Автору предлагаю остановится тогда когда получится стабильно забивать свой канал под 99%, с минимальными затыками (3-5 сек в минуту) аааа себе предлагаю порытся в старых исходниках и написать либу под ZF заодно портировав под пхп5, со стрим_селект работать вроде поудобнее будет
+4
ааа и ну конечно в идеале держатся подальше от pctl который много где не стоит и следить за использованием ресурсов. У меня использовался только канал, процессор и память никак напрягались.
0
я тоже доставлял себя много анальной боли костылями, плюясь на отвратный курл, плохие доки, неведомые глюки с таймаутами и прочую кривоту, потом просто взял эрланг и за вечер набросал отлично контролируемый кравлер со всеми таймаутами и прочим. работает быстрее, надежнее и гибче курла.
0
Хороший велосипед — будем ездить! Спасибо!
-2
пока вы пишете велосипеды, в мире перл уже давно все написано
LWP::Parallel
LWP::Parallel
-3
а заминусовали видимо от зависти, что в мире Perl есть такое централизованная система обмена полезным кодом, в которой хорошим тоном считается снабдить модуль документацией и тестами (которые к слову запускаются в автоматическом режиме на куче разных платформ чтобы уведомить автора о потенциальных проблемах) и также принимать багрепорты.
+1
Вы не поверите, но в мире php это тоже есть =)
LWP — мощный инструмент, знаю не по наслышке, но речь не о нем, а ваш комментарий немного разошелся с темой обсуждения и провоцирует к холиварам. Реакция на лицо
LWP — мощный инструмент, знаю не по наслышке, но речь не о нем, а ваш комментарий немного разошелся с темой обсуждения и провоцирует к холиварам. Реакция на лицо
+1
Eсть, но не очень впечатляет, к сожалению (если вы про pear/pecl) :(
Просто выкладывать очередной написанный класс на хабр и говорить вот мол написал как-то так — потом получается что проект состоит из «скопировал с хабра», «скопировал с какого-нибудь еще сайта».
На мой взгляд интерфейс у класса слегка страдает, отсутствует возможность настройки какой-либо.
Почему бы автору не оформить это в законченный продукт — сделать минимальную документацию — выложить на github, google code, на тот же pear.
При этом можно посмотреть есть ли какая-нибудь уже хорошая библиотека для отправки http/ftp запросов и нельзя ли использовать к примеру threads c ней.
Просто выкладывать очередной написанный класс на хабр и говорить вот мол написал как-то так — потом получается что проект состоит из «скопировал с хабра», «скопировал с какого-нибудь еще сайта».
На мой взгляд интерфейс у класса слегка страдает, отсутствует возможность настройки какой-либо.
Почему бы автору не оформить это в законченный продукт — сделать минимальную документацию — выложить на github, google code, на тот же pear.
При этом можно посмотреть есть ли какая-нибудь уже хорошая библиотека для отправки http/ftp запросов и нельзя ли использовать к примеру threads c ней.
+1
улыбнуло в комментах кода — «На всякий случай»
на какой такой всякий случай? (с) =)))
на какой такой всякий случай? (с) =)))
-2
А вот здесь рахве нет решения Вашей поблемы
www.onlineaspect.com/2009/01/26/how-to-use-curl_multi-without-blocking/
?
www.onlineaspect.com/2009/01/26/how-to-use-curl_multi-without-blocking/
?
+5
Хорошее решение, проверю на очень медленных соединениях. Спасибо.
0
> $socket = socket_create(AF_INET, SOCK_STREAM, );
вот тут опечатка, скорее всего побилось в парсере, наверное надо дописать SOL_TCP как третий параметр
я бы на Вашем месте изменил бы имена get, ну и post за ним. Лично для меня get означает что-то получить из класса, а не метод запроса для хттп. Возможно это просто дело привычки, но мне кажется более уместными были бы имена getRequest, postRequest или вроде того.
вот тут опечатка, скорее всего побилось в парсере, наверное надо дописать SOL_TCP как третий параметр
я бы на Вашем месте изменил бы имена get, ну и post за ним. Лично для меня get означает что-то получить из класса, а не метод запроса для хттп. Возможно это просто дело привычки, но мне кажется более уместными были бы имена getRequest, postRequest или вроде того.
0
Если бы это было написано на С с BSD-сокетами, я бы сказал что это пример того, как делать не надо.
Может быть на PHP так принято?
Скорее происходит, но не сразу. А сразу возвращается ошибка, потому что операция потенциально блокирующая.
Ни одной проверки на то, что socket_write записал все, что его просили записать. Он и в блокирующем-то режиме судя по документации может записать не всё, а в неблокирующем тем более.
Опять же, никакой проверки длины. Наверно это будет работать для мелких ответов. Но по чистой случайности.
Воспользуйтесь socket_select, чтобы отсеять большую часть «всяких» случаев.
Может быть на PHP так принято?
// Если установить флаг до socket_connect соединения не происходит
Скорее происходит, но не сразу. А сразу возвращается ошибка, потому что операция потенциально блокирующая.
socket_set_nonblock($socket);
socket_write($socket, $method." ".$parts['path']." HTTP/1.1\r\n");
socket_write($socket, «Host: ».$parts['host']."\r\n");
socket_write($socket, «Connection: close\r\n»);
if ($data) {
socket_write($socket, «Content-Type: application/x-www-form-urlencoded\r\n»);
socket_write($socket, «Content-length: ».strlen($data)."\r\n");
socket_write($socket, "\r\n");
socket_write($socket, $data."\r\n");
}
socket_write($socket, "\r\n");
Ни одной проверки на то, что socket_write записал все, что его просили записать. Он и в блокирующем-то режиме судя по документации может записать не всё, а в неблокирующем тем более.
$data = socket_read($socket, 0xffff);
if ($data) {
$threads[] = $key;
$this->setThread($key, $data);
unset($this->sockets[$key]);
continue;
Опять же, никакой проверки длины. Наверно это будет работать для мелких ответов. Но по чистой случайности.
// На всякий случай
usleep(5);
Воспользуйтесь socket_select, чтобы отсеять большую часть «всяких» случаев.
+4
Не гуглить — здоровью вредить.
code.google.com/p/multicurl-library/
code.google.com/p/multicurl-library/
0
не читать предыдущие комментарии тоже плохо ;)
0
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
НЛО прилетело и опубликовало эту надпись здесь
2009 =). По вашей статье разберусь с сокетами.
Никогда не поздно — Спасибо!
Никогда не поздно — Спасибо!
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Работа с http через неблокируемые сокеты