Need help to design web crawler.
слишком толсто
Why don't you take some existing crawler library like Könguló as a basis? You may also look at this thread:
Thanks you, but i need a good design. I know about Larbin, DataparkSearch (author is a VMK MSU) but it don't help me.
государственный поисковик отаке?
What do you consider to be a good design? Have you read
http://www.slideshare.net/gnap/design-and-implementation-of-... but i want to find more detail.
yes, i've read it, and also this
национальный™
у кого нибудь есть документацию для того чтобы проектировал хороший webcrawler? Мне нужно чтобы он быстро скачал(сотни за секунду).Самый крутолько качальщик — это Бачан, он сечет круче всех ИМХО. Но так как Бачан не всегда хочет общаться, то могу я рассказать основы.
Во-первых, качать нужно только в один поток. Потоков может быть несколько, один в том смысле, что надо неблокирующе качать.
1: Надо ли писать на диск? Если надо, то число качающих потоков по числу дисков (физических).
Разумеется, надо использовать для скачивания curl, он прекрасно работает как в примитивном режиме, так и на мультплексе. Бачан даже его с libev скрещивал, что просто гениально.
Во-вторых, лучше всего первичную обработку делать сразу, а не писать на диск. Чаще всего надо что-то взять со странички, проиндексировать или что-то ещё, очень редко надо сохранять всё скачанное.
2: Насколько сложная обработка данных? Данные лучше обрабатывать сразу, число потоков — по числу ядер в системе.
Не повредит хороший балансёр, отправляющий на скачку. Нужно отправлять ровно столько урлов на скачивание, сколько будет успевать обрабатываться.
Если всё сделать так как я описал, то дальше тонкости ещё есть. Опиши что делаешь, что качаешь — может ещё что-то подскажу.
И да, почему в один поток-то качать? Чаще всего этого хватает, конечно, но бывают ситуации, когда даже тупая асинхронная качалка во что-то упирается, т.к. top показывал для качающего потока большую загрузку проца, то думаю, что в проц. Но такое редко бывало, и наверное просто из-за некоторой обработки по ходу скачивания.
curl хреново работает в мультиплексе, у него проблемы ловить таймауты. Кое-как прикрутил их ловлю в сам libevent, но это довольно костыльное решение. Без него таймаутящаяся закачка могла подвисать и больше о себе не напоминать никакими событиями.Слушай, а скинь мне в личку, интересно, я не знал о таком деле.
И да, почему в один поток-то качать?
Под одним потоком я имел скорее в виду "конечное число по числу ядер". А так вот Возбу, тоже уже специалист по закачке.
Топикстартер, ау! Ты где?
curl хреново работает в мультиплексе, у него проблемы ловить таймауты. Кое-как прикрутил их ловлю в сам libevent, но это довольно костыльное решение. Без него таймаутящаяся закачка могла подвисать и больше о себе не напоминать никакими событиями.я сначала хотел ответить по существу, а потом заметил (внезапно
что абзац написан "в стиле пианиста" и это прикольно.
по существу.
если я правильно понимаю, курл предполагает мультиплекс типа селект(2 когда все почти как в easy
и мультиплекс типа ваш-собственный-евент-драйвер(7 когда евент-драйвер - ваш, собственный, костыльный.
то есть, с libevent там все вроде бы так и задумано делать, как ты делаешь, и "без него таймаутящаяся (ааааа,
какое слово охуенное) закачка могла подвисать", в случае libevent, makes no sense, потому что собственный-
евент-драйвер(7) без обработки таймаутов по DIY-принципу makes no sense either.
короче, я ничего не понял из того, что ты написал, но решил, тем не менее, поделиться мыслями
и ссылкой на мою любимую статью про таймауты, которую написал марк леманн:
http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#code_ev...
интересная часть называется "Be smart about timeouts".
прекрасное название!
что касается подвисаний вообще, вот это полезно помнить,
как мне кажется (часть про EVBACKEND_EPOLL):
http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#FUNCTIO...
для скачания страницы, я собираю написать на python. Это часть называется downloader ещё часть называется crawlApp для обработки данных и сохранении новых урлов в очереди (или БД) от сюда downloader будет скачать с этими урлами. Эти части будет разговаривать через сокет. Как Вы думаете так нормально?
хочется считать 10 страниц, тысячу, миллионы?
Поведаю историю вопроса. За основу был взять один твой демон, который так никогда и не эксплуатировался. Из него я выдрал curl'о-libevent'овский механизм и обернул в классик. Таймауты там выставлялись через curl_easy механизм (curl_easy_setopt) для каждой закачки, т.е. на DNS, transfer и connect. Кстати, прикольно, щас в мане увидел
CURLOPT_LOW_SPEED_TIME
Pass a long as parameter. It contains the time in seconds that the transfer should be below the CURLOPT_LOW_SPEED_LIMIT for the
library to consider it too slow and abort.
Ну вот. Это попытались эксплуатировать, но качало очень плохо, там были десятки или сотни одновременных закачек, и много из них фейлилось. Я перекопал весь код, перечитал по использованию curl_multi манов и примеров в интернете, мне казалось, что все правильно. Когда я уже окончательно сломал голову, админы подняли в конфиге тайм-аут, и стало работать гораздо лучше
Вывод отсюда такой - таймауты в multi в том виде, в котором я использовал, есть. При этом libevent на сокеты сам таймауты не ставил, все делал curl, он мог выставить закачке код ошибки CURLE_OPERATION_TIMEDOUT.
Потом однажды я заметил, что есть висящие закачки. Не помню точно, что происходило, но они висели долго, и никаких событий на сокете не происходило.
Я почесал репу, сходил на сайт курла, как раз вышла версия 7.20.0, почитал Changelog (http://curl.haxx.se/changes.html увидел строчку
CURLM_CALL_MULTI_PERFORM fix for multi socket timeout calls, обрадовался, собрал все с этим курлом.
Вроде стало лучше работать, но точно не помню, проблемы все равно были, поэтому я, тем не менее, повесил таймаут на качающий сокет, как сумму таймаутов connect+transfer, и в итоге закачку теперь обрубает libevent, если это не может сделать curl.
Возможно, конечно, я где-то что-то не так понял или что-то упустил, но вот так оно сейчас работает. Может быть на самом деле закачка просто идет очень медленно, а курловский таймаут ставится не на всю закачку, а только на период, когда данные не приходят совсем, и каждый чанк данных его сбрасывает. Хотя, на мой взгляд, это противоречило бы ману, который обещает таймаут на весь трансфер:
CURLOPT_TIMEOUT
Pass a long as parameter containing the maximum time in seconds that you allow the libcurl transfer operation to take. Normally,
name lookups can take a considerable time and limiting operations to less than a few minutes risk aborting perfectly normal oper-
ations. This option will cause curl to use the SIGALRM to enable time-outing system calls.
In unix-like systems, this might cause signals to be used unless CURLOPT_NOSIGNAL is set.
дефайн CMS curl_multi_socket(3)
дефайн CMSA curl_multi_socket_action(3)
Я когда переходил с CMP на CMSA, вся документация по последним противоречила
действительности, и я пошел читать архив рассылки:
http://news.gmane.org/gmane.comp.web.curl.library
Там тогда как раз появился пример использования нового интерфейса (в текущей
версии deprecated-вызовов CMS нет, вместо них используется CMSA с нулевой
ev_bitmask, то есть на устаревшую версию можно смело забить):
http://curl.haxx.se/libcurl/c/hiperfifo.html
И вот из него-то я и сделал вывод о том, что, во-первых, CMSA дает программисту
возможность управлять таймаутами так же гибко, как если бы curl-абстракции не
было вовсе, а во-вторых, что это один из поинтов CMSA-интерфейса - разным
прикладным областям нужны разного рода таймауты, поэтому для тех, кого не
устраивает просто таймаут на всю операцию, мы придумали CMSA. Это касается
любых эвентов на сокете, а не только таймаутов - гибкая обработка в обмен на
усложнение кода.
Не очень давно hiperfifo.c какой-то чувак из рассылки портировал на libev,
пианист про него, я думаю, говорил в первом своем сообщении:
http://curl.haxx.se/libcurl/c/evhiperfifo.html
Теперь, чтобы понять, нужны ли нам гибкие таймауты или мы можем забить на being
smart about them и пользоваться CURLOPT_*TIMEOUT, нам нужно ясно представлять
себе, чем мы рискуем in the latter case.
Okay, Let's Go (c):
CURLOPT_TIMEOUTМетод используемый курлом для выставления таймаутов - это полный пиздец. This
Pass a long as parameter containing the maximum time in seconds that you allow
the libcurl transfer operation to take. Normally, name lookups can take a
considerable time and limiting operations to less than a few minutes risk
aborting perfectly normal operations. This option will cause curl to use the
SIGALRM to enable time-outing system calls.
In unix-like systems, this might cause signals to be used unless
CURLOPT_NOSIGNAL is set.
option will cause curl to use the SIGALRM to enable time-outing system calls.
Иными словами, минимальный код (без обработки ошибок) какой-то такой:
{
sigfunc *sf;
sf = signal(SIGALRM, conn_alrm);
alrm(CURLOPT_CONNECT_TIMEOUT);
if (0 > connect(sd, ...
{
close(sd);
if (EINTR == errno)
{
errno = ETIMEDOUT;
}
}
...
}
static void
conn_alrm(int signo)
{
return;
}
Первая проблема - перегрузка EINTR, кто-то может его использовать и тогда нам
надо либо делать sigsetjmp(3) и siglongjmp(3 что само по себе довольно хорни,
либо какие-то свои дескрипторы/идентификаторы придумывать, что я даже боюсь
начинать делать, потому что предполагаю, что это невозможно.
Вторая - в мультитредных приложениях SIGALRM приходит в зависимости от
блокировок сигналов и прочей шляпы хуй знает в какой тред.
В принципе, у меня уже взорвался мозг от того, что там может получиться только
засчет этой пары опасностей, но я могу попробовать придумать еще!
###############################################################################
"Внезапно" - это в стиле Максима. Ты специально?ну меня просто развеселило неожиданное наблюдение: твой пост показался
не как обычно обстоятельным и богатым деталями, а списком хрен знает как,
на первый взгляд, связанных между собой, но на самом деле важных тезисов. =)
когда я утром перечитал, впечатление несколько ослабло, though.
CURLOPT_LOW_SPEED_TIMEМне кажется, эту фичу для всяких видео/аудио-стриминг-приложений сделали. Не
Pass a long as parameter. It contains the time in seconds that the transfer
should be below the CURLOPT_LOW_SPEED_LIMIT for the library to consider it too
slow and abort.
глядя в код, конечно, хрен его знает, как оно на html-страницах себя поведет.
Звучит, как сложная логика, прикручивая которую норвежские программисты
наверняка опять все сломали. Но чем черт не шутит... может что-то вдруг и
починили.
В APUE про alrm-технику не было этих тонкостей.
Весной, кстати, научились брауни делать по рецепту Стивенса.
http://www.kohala.com/start/recipes/deirdre.brownie.html
Охуенный. Попробуйте тоже.
ну вернее научилАсь, конечно.
для скачания страницы, я собираю написать на pythonthe important thing is technique, not technology. python has a lot of modules with
the same functionality (on a brief view but completely different purposes. so you
should know, for example, which of html-processing modules fits your purposes
well to use python.
и вместо TCPv1 тоже ничего, без которого TCPv2 программисту хуже пойдет имхо.
выпросьте у злых плантаторов стивенса всего хотя бы.
а то левис не фанат TCP/IP, а больше я не знаю, кто зачетные книги приносил.
Как ты из примера http://curl.haxx.se/libcurl/c/hiperfifo.html сделал выводы и гибкости CMSA? В смысле, кто мешал не использовать в старых интерфейсах никаких таймаутов и делать все таймауты при обработке событий самому?
Кстати, мне еще странно, что они в своих примерах на каждую закачку создают curl_easy_init и уничтожают curl_easy_cleanup. Возможно, в силу простоты примеров, но однажды профилировщик показал, что эти операции занимают немало времени, а надобности в них никакой, поэтому я предпочел сделать пул этих хэндлеров, переиспользовать их быстрее.
На тему CURL_*_TIMEOUT и сигналов - как-то в man неоднозначно сказано, по-моему:
This option will cause curl to use the
SIGALRM to enable time-outing system calls.
In unix-like systems, this might cause signals to be used unless
CURLOPT_NOSIGNAL is set.
Из второго предложения я делаю вывод, что при установке флага CURLOPT_NOSIGNAL сигналы использоваться не будут. Хотя из первого делаю вывод, что будут
На самом деле, может быть все работает, потому что сначала все сигналы блокирую, потом рождаю дочерние потоки, они наследуют блокированность сигналов, потом уже в главном потоке расставляю обработчики сигналов, и только ему они приходят. При этом curl в своем потоке может разрешить SIGALRM, и он тогда будет приходить только в этот поток. Но все равно мне кажется, что он сигналы не использует с CURLOPT_NOSIGNAL.
Еще наткнулся в коде на коммент, ведущий меня к known bug (http://curl.haxx.se/docs/knownbugs.html):
62. CURLOPT_TIMEOUT does not work properly with the regular multi and
multi_socket interfaces. The work-around for apps is to simply remove the
easy handle once the time is up. See also:
http://curl.haxx.se/bug/view.cgi?id=2501457
Охуенный. Попробуйте тоже.Рецепт жесть.
а то левис не фанат TCP/IPО, он проповедует IPX?
как почти всегда бывает, угадал лучше, потому что знал про мегасхему!
мегасхема: http://www.j3qq4.org/pic/megaschema.gif
но вообще сказал, что ему этим не оч прикольно заниматься.
он по всякому сжатию данных рубится вроде.
Рецепт жесть.ыыы! в каком смысле? =)
мы его адаптировали под суровые российские реалии,
я пришлю, если хочешь, он на бумажке просто. =)
Как ты из примера http://curl.haxx.se/libcurl/c/hiperfifo.html сделал выводы иА как?
гибкости CMSA? В смысле, кто мешал не использовать в старых интерфейсах никаких
таймаутов и делать все таймауты при обработке событий самому?
Кстати, мне еще странно, что они в своих примерах на каждую закачку создаютОни неаккуратные. Либу читать невозможно.
curl_easy_init и уничтожают curl_easy_cleanup. Возможно, в силу простоты
примеров, но однажды профилировщик показал, что эти операции занимают немало
времени, а надобности в них никакой, поэтому я предпочел сделать пул этих
хэндлеров, переиспользовать их быстрее.
Пул прекрасное решение!
Теоретически можно было бы заставить курл делать динамические аллокейты
(предполагаю, что они в клинапе тормозят) как тебе хочется и эффективно
распределять уже саму память, но это сложно и нужно мб только тем, кто хочет
память очень-очень экономить (предполагаю, что хендлеры из пула до клинапа не
освобождают память, выделенную под мегабайтную страницу, поэтому когда мы потом
в этом хендлере качаем килобайтную, мы вроде как неэффективно память
юзаем).
На тему CURL_*_TIMEOUT и сигналов - как-то в man неоднозначно сказано,Wow! Ты прав, я сначала невнимательно прочитал про NOSIGNAL. Пугает то, что в
по-моему:
This option will cause curl to use the
SIGALRM to enable time-outing system calls.
In unix-like systems, this might cause signals to be used unless
CURLOPT_NOSIGNAL is set.
Из второго предложения я делаю вывод, что при установке флага CURLOPT_NOSIGNAL
сигналы использоваться не будут. Хотя из первого делаю вывод, что будут.
мане не написано, что делать, если заданы и таймаут, и носигнал. Возвращать
ошибку из setopt-а? Не получится (носигнал может быть задан позже и у него
приоритет). Не использовать таймауты? Ты говоришь, что они вроде бы срабатывают.
Пойду потом тоже код почитаю, видимо. =)
Еще есть надежда, что в мане опечатка и таймауты всегда ставятся с помощью ALRM,
тогда с ними все прозрачно.
...
На самом деле, может быть все работает, потому что сначала все сигналы блокирую,Ага, мне теперь тоже так кажется. Прикольно, тем не менее, что сигналы,
потом рождаю дочерние потоки, они наследуют блокированность сигналов, потом уже
в главном потоке расставляю обработчики сигналов, и только ему они приходят. При
этом curl в своем потоке может разрешить SIGALRM, и он тогда будет приходить
только в этот поток. Но все равно мне кажется, что он сигналы не использует с
CURLOPT_NOSIGNAL.
видимо, тоже будут работать. Хотел там race condition найти где-нибудь,
а его и нету походу.
Еще наткнулся в коде на коммент, ведущий меня к known bug (http://curl.haxx.se/docs/knownbugs.html):Ссылка крутая.
62. CURLOPT_TIMEOUT does not work properly with the regular multi and
multi_socket interfaces. The work-around for apps is to simply remove the
easy handle once the time is up. See also:
http://curl.haxx.se/bug/view.cgi?id=2501457
Что касается таймаута операции: а что он характеризует в случае неблокирующегося
скачивания нескольких файлов одновременно? По идее нужен таймер молчания на
каждый сокет.
Шутки ради, можно еще "общее" "время" молчания считать, это будет,
видимо, аналогом таймаута операции в блокирующемся режиме.
если сравнивать со временем между двумя последовательными чтениями
в контексте производительности сети.
часто бывает так, что размер данных почти не колеблется от запроса к
запросу, поэтому можно заменить "правильный" таймаут между чтениями
на более понятный эмпирически таймаут операции.
но вообще делая так можно потерять большие страницы без особых на то
причин.
часто бывает так, что размер данных почти не колеблется от запроса кЯ уже не очень хорошо все помню, но смотри: когда CURL говорит, что на сокет надо поставить событие, я его ставлю с таймаутом. Событие ставится не как EV_PERSIST, поэтому после его наступления его надо будет перепоставить. Т.к. я сам не переставляю событие в его обработчике, значит, CURL после наступления события опять вызывает свой колбек для сообщения мне, что надо поставить событие, и я его ставлю. Таким образом, таймаут устанавливается заново каждый раз, когда приходят данные, т.е. реализуется "правильный" вариант, хотя на самом деле я тогда желал сделать "неправильный", но вышло, что вышло.
запросу, поэтому можно заменить "правильный" таймаут между чтениями
на более понятный эмпирически таймаут операции.
предполагаю, что хендлеры из пула до клинапа неА почему CURL вообще должен держать в памяти мегабайтную страницу? Тебе, конечно, виднее, раз ты его внутренности читал, но я считал, что процесс происходит так:
освобождают память, выделенную под мегабайтную страницу, поэтому когда мы потом
в этом хендлере качаем килобайтную, мы вроде как неэффективно память
юзаем
на сокет приходят данные, срабатыавет триггер, он вызывает курловые функции, тот вызывает recv на свои 16 кб, пока есть данные, для каждых 16 кб данных вызывается пользовательский колбек. Т.е. данные кочуют с сетевой карты в 16 кб буфер курла (ну или чуть больше в случае разжатия данных и оттуда сразу в пользовательский колбек. Где они тут могут храниться?
про мегабайт - это "грубо говоря" все, забей на серый текст
с этими оговорками звучит все хорошо вроде.
если при этом не использовать CURLOPT_*TIMEOUT,
то ошибки должны совсем исчезнуть.
не парься даже, это не костыль ни разу! =)
ощущение костыля мб разве что потому что сам курл - это один большой костыль.
Оставить комментарий
kolmem
Превет всем,у кого нибудь есть документацию для того чтобы проектировал хороший webcrawler? Мне нужно чтобы он быстро скачал(сотни за секунду).