![]() |
Модераторы: korob2001, ginnie |
![]() ![]() ![]() |
|
HeinzFelfe |
|
|||
Новичок Профиль Группа: Участник Сообщений: 18 Регистрация: 15.9.2012 Репутация: нет Всего: нет |
Здравствуйте.
Возникла необходимость получить время затраченно на посылание запроса и ожидания ответа. http_request'ы делаются в цикле, по элементам массива с url-адресами. Т.е. если бы массив был маленький, равный значению $AnyEvent::HTTP::MAX_PER_HOST, то можно было бы просто перед циклом делать вызов time, и потом в обработчике http_request ещё один time, и из последнего вычесть первый. Но если размер массива много больше $AnyEvent::HTTP::MAX_PER_HOST, то такой пусть кривой, но все таки способо, не сработает. Не нашёл информации о том как это мжно реализовать. По моим соображениям, что бы получить эту информацию нужно писать свой вариант tcp_connect
что бы непосредственно в нем для каждого соединения вызывать time в начале и в конце его кода. Но как его писать я честно говоря представляю слабо... Да и возможно есть более легкие и очевидные способы получения времени ожидания ответа? |
|||
|
||||
Pfailed |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 933 Регистрация: 19.7.2009 Репутация: 22 Всего: 39 |
Легче использовать опцию on_prepare. Но нужно учесть, что это не будет работать для повторных keepalive соединений. Поэтому persistent устанавливаем в ноль.
|
|||
|
||||
HeinzFelfe |
|
||||
Новичок Профиль Группа: Участник Сообщений: 18 Регистрация: 15.9.2012 Репутация: нет Всего: нет |
Эта штука работает уже лучше) Но все равно не корреткно. Представим, что у нас в массиве 100 хостов на которые мы должны отправить get-запрос. Представим, что у нас $AnyEvent::HTTP::MAX_PER_HOST=20, т.е. за один раз отправленно 20 запросов, и для каждого из них $start_time = time; будет один и тот же. И даже если предложить, что ответ от этих 20 запросов придет оновременно, то вызов колбэка (в котором мы считаем время итоговое):
для кождого из них будет происходить последовательно, т.е. не сразу 20 одновременных my $total_time = time - $start_time;. А будет задержка, и при значительных размерах массива, задержка будет так же значительной. К примеру для массива из 86 адресов, и с заданным таймаутом 4 секунды, для некоторых адресов итоговое время получается 5, 6, 7 а то и все 9 секунд. А такого в принципе быть не должно, ну так как таймаут всего 4 секунды. Т.е. пока что, все указывает на то, что подсчет времени должен выпонляться именно в своем варианте tcp_connecte (который нужно самому написать). А как его писать я пока-что не разобрался, увы, английский мой оставляет желать лучшего, а все гайды только на нем. Есть у вас мысли какие-нибудь? |
||||
|
|||||
Pfailed |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 933 Регистрация: 19.7.2009 Репутация: 22 Всего: 39 |
Таймаут в 4 секунды не означает, что запрос завершится не позднее, чем через 4 секунды. Посмотрите описание опции timeout, там сказано, что это таймаут на каждую операцию в отдельности. Т.е. если connect() займет 2 секунды, а потом read из сокета еще 3 (а таких вызовов может быть много и у каждого таймаут считается сначала), то запрос не уложится в предполагаемые 4 секунды. В общем случаи таймаут + 10 секунд это норма. Это больше похоже на причину полученных вами 9 секунд, чем то, что вызов 86 коллбеков занял 5 секунд. Хотя тут конечно нужно смотреть на код этих коллбеков. |
|||
|
||||
HeinzFelfe |
|
||||||||
Новичок Профиль Группа: Участник Сообщений: 18 Регистрация: 15.9.2012 Репутация: нет Всего: нет |
Что бы не быть голословным вот пример кода.
Правда в процессе появилась ещё одна стремная проблема... о ней после. В 50 строке, в колбэке, я ставлю sleep 4 секунды (равный значению таймаута, или больше него, это для второй проблемы актуально).
И в результате выполнения получаем вот такие значения времени
Т.е. видно что узкое место тут это время выпонения колбэка нашего основного, т.е. процедура подсчета времени должна быть сделана до его вызова. А теперь о второй проблеме... Как только я постаавил sleep, то вывод скрипта с вот такого приятного и правильного
Сменился сплошными ошибками
Т.е. получается... пока один колбэк выполлняется, в это время в фоне висят соединения, для которых тикает тайм-аут, и пока выполняется колбэк, таймаут может истечь, и мы получим такую вот ерунду 595, reason: Время ожидания соединения истекло... Эээээ, а как тогда быть?! Где тогда задавать тайм-аут, что бы он был у каждого соединения своим? Или я что-то не так понял? Это сообщение отредактировал(а) HeinzFelfe - 16.12.2012, 15:35 |
||||||||
|
|||||||||
Pfailed |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 933 Регистрация: 19.7.2009 Репутация: 22 Всего: 39 |
Любой sleep губителен для событийно-ориентированной программы. Коллбек должен отрабатывать максимально быстро, т.к. все оперции выполняются в рамках одного процесса и потока. Пока вы делаете sleep вся программа стопорится.
Кстати, в вашем примере отсутствует опция timeout, а таймаут по умолчанию 5 минут.
Опция timeout и задает таймаут для каждого соединения, просто не нужно писать неправильные коллбеки, работающие слишком долго. |
|||
|
||||
HeinzFelfe |
|
||||
Новичок Профиль Группа: Участник Сообщений: 18 Регистрация: 15.9.2012 Репутация: нет Всего: нет |
Так вроде же колбэк on_prepare должен возвращать таймаут? Он у меня его и возвращает.
Хм... А как тогда быть с задачей когда в коллбеке необходимо обрабатывать или проверять данные, например: Мне нужно узнать, доступен ли некий ресурс (пусть google.com) из некоторой страны. Я нахожу несколько прокси-серверов для этой страны (но большинство прокси лежащих в отркытом доступе - штуки ненадежные и маложивущие, т.е. чем больше список прокси-серверов тем лучше). Пусть у меня есть 100 прокси Гандураса. И вот я посылаю запросы на google.com через каждый прокси (в цикле все это дело реализую). При получении кода ответа 200 мне ненужно ждать ответы от остальных запросов, т.к. на очереди у меня могут стоятть ещё 100 стран,. Допустим от одного из прокси-сервером мне приходит ответ 200, но прокси эта такая штука ненадежная, что они могут прислать ответ 200 и от несуществующего ресурса, и доверять таким прокси-серверам нельзя. Возникает ситуация, когда нужно сделать проверку (о её устройстве и принципах умолчим). Она занимает существенное время, большее таймаута. И если проверка не пройдена, то нужно выполнять следующий запрос, а тайм-аут к этому времени уже помер, и начинаются чудеса с ошибкой 595. Как быть в такой ситуации? Придется сначала получить ответы от всех 100 запросов, сохранить в переменной адреса прокси для которых пришел ответ 200, и уже после выполнения всех запросов вне http_request'а и его колбэка начинать проверять этот список? Но это будет много дольше. Т.к. задержка между Россией и Гонудрасом может быть большой, да ещё и прокси-загружен - одного ответа можно ждать и 10 и 20 секунд и больше, а для нескольких стран, по 100 прокси в каждой, мы уже полчуаем неплохие задержки по времени. Или деваться некуда и придется жертвовать скоростью выполнения? Это сообщение отредактировал(а) HeinzFelfe - 16.12.2012, 16:23 |
||||
|
|||||
Pfailed |
|
|||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 933 Регистрация: 19.7.2009 Репутация: 22 Всего: 39 |
on_prepare возвращает таймаут только для операции connect() Всё зависит от секретной проверки. Скорее всего ее тоже можно сделать через anyevent и тогда задача решена. Если вдруг нет, то можно форкать для этой проверки отдельный процесс. |
|||
|
||||
HeinzFelfe |
|
||||
Новичок Профиль Группа: Участник Сообщений: 18 Регистрация: 15.9.2012 Репутация: нет Всего: нет |
Касаемо проблемы со странными ошибками 595 и 596.
Как я и думал проблема оказалась не в якобы тяжелых колбэках, которые якобы не могут быть очень сложными, и должны выполняться быстренько (смысл в них тогда вообще...). Можно хоть 100 sleep'ов вставить по 100 секунд, все равно все будет работать так как надо. Дело оказалось вот в чем. На моем примере расскажу, для тех кто, вдруг, с подобным столкнется. Пусть у нас есть список ресурсов ya.ru, google.com, bing.com, и есть список прокси-серверов, например, из 100 штук. Задача состоит в том, что бы для каждого ресурса определить доступен он хотя бы через один из этих прокси, или нет. И так у нас есть функция sub доступен_ли_ресурс. Вызываем её, передав ей в качестве параметров первый ресурс ya.ru и массив из проксей. В нутри процедуры в цикле по значениям массива с прокси начинаем http_request'ами слать запросы через каждый прокси, как только нам придет ответ 200, нужно завершить процедуру, недожидаясь остальных ответов. Следующий шагом мы опять вызуываем sub доступен_ли_ресурс в качестве параметров - следующий ресурс google.com, и снова массив из 100 прокси. Начинам делать запросы и тут... вылезают всякие гадости с кодом ошибки 595 или 596. А проблема вот в чем... Когда мы досрочно завершаем первый вызов функции, то внутри тех tcp_connect'ов, ответа от которых мы не дождались остался жив таймер, который задается значением таймаута. Подробности в файле socket.pm библиотеки anyevent::socket, кратко приведу код из неё: my $timeout = $prepare && $prepare->($state{fh}); $timeout ||= 30 if AnyEvent::WIN32; $state{to} = AE::timer $timeout, 0, sub { $! = Errno::ETIMEDOUT; $state{next}(); } if $timeout; # now connect if ( (connect $state{fh}, $sockaddr) || ($! == Errno::EINPROGRESS # POSIX || $! == Errno::EWOULDBLOCK # WSAEINPROGRESS intentionally not checked - it means something else entirely || $! == AnyEvent::Util::WSAEINVAL # not convinced, but doesn't hurt || $! == AnyEvent::Util::WSAEWOULDBLOCK) ) { $state{ww} = AE::io $state{fh}, 1, sub { # we are connected, or maybe there was an error if (my $sin = getpeername $state{fh}) { my ($port, $host) = unpack_sockaddr $sin; delete $state{ww}; delete $state{to}; my $guard = guard { %state = () }; $connect->(delete $state{fh}, format_address $host, $port, sub { $guard->cancel; $state{next}(); }); } else { if ($! == Errno::ENOTCONN) { # dummy read to fetch real error code if !cygwin sysread $state{fh}, my $buf, 1; # cygwin 1.5 continously reports "ready' but never delivers # an error with getpeername or sysread. # cygwin 1.7 only reports readyness *once*, but is otherwise # the same, which is actually more broken. # Work around both by using unportable SO_ERROR for cygwin. $! = (unpack "l", getsockopt $state{fh}, Socket::SOL_SOCKET(), Socket::SO_ERROR()) || Errno::EAGAIN if AnyEvent::CYGWIN && $! == Errno::EAGAIN; } return if $! == Errno::EAGAIN; # skip spurious wake-ups delete $state{ww}; delete $state{to}; $state{next}(); Красным отмечена инициализация тайм-аута и таймера связанного с ним. Очевидно, что если мы не будем ждать осталных ответов, то вот эта строчка (а их там две таких фиолетовеньких, перва сбрасывает таймаут когда соеденинее прошло хорошо, а вторая когда случилась какая-нибудь ошибка)
Для решения этой проблемы я вижу два решения. 1. Крутое. Написать свой tcp_connect со всеми плюшками и обработчиками всяких нехороших ситуаций. 2. Простое. После вызова такой функцииsub доступен_ли_ресурс, вставлять функцию sub ловушка_для_не_прибитого_таймера. Которая будет выглядеть примерно так:
использование http://127.0.0.1/ это на всякий случай, вдруг наша первая функция sub доступен_ли_ресурс отработает вся полностью, и с таймаутами там все будет хорошо, и что бы не стопариться на ожидании ответа от какого-нибудь ресурса лишних милисекунд мы постучимся к себе localhost. Так вот эта функция на себе испытает всю прелесть криво завершенных tcp_connect'ов из передыдущей функции, кое-как отрботает быстренько, нормально завершит tcp_connect и прибьет таймер как надо. И уже следующий вызов многострадальной sub доступен_ли_ресурс, пройдет как по маслу. Решение костыльное, но рабочее и времени на свое выполнение много не занимающее. Но в идеале, конечно же, свой tcp_connect. За некоторый сумбур в изложении и наличее очепяток извиняюсь. P.S. Учите английский, товарищи, и решение подобных проблем займет у вас куда меньше веремени чем у меня, с моими куцими знаниями... Это сообщение отредактировал(а) HeinzFelfe - 19.12.2012, 02:16 |
||||
|
|||||
Pfailed |
|
||||
Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 933 Регистрация: 19.7.2009 Репутация: 22 Всего: 39 |
Как в таком случае объясните результат выполнения этого кода
|
||||
|
|||||
![]() ![]() ![]() |
Правила форума "Perl" | |
|
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, korob2001, sharq. |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Perl: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |