Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Perl: разработка для Web > Anyevent::HTTP


Автор: HeinzFelfe 6.12.2012, 18:01
Преступил к изучению anyevent::http.
Возникают вопросы, может кто сталкивался с подобным уже.

И так, в предпологаемой программе берется спискок прокси-серверов (пусть их там будет 1000 штук), и по каждому из них, через цикл, выполняется get запрос.
Необходимо при получении кода состояния HTTP '200 ok' выходить из цикла и не дожидаться ответа от уже посланных запросов.

Как это сделать я не понмаю.

У меня получается так: приходит ответ 200 ok, вызываю $cv->cend, но цикл продолжает выполняться. 
использовать exit нельзя, т.к. мы должны остаться в программе. 
return так же не работает.

Как быть?

p.s. в английском не силён, так что чтение мануалов официальных весьма затруднено...

Автор: DurRandir 6.12.2012, 18:13
Цитата(HeinzFelfe @ 6.12.2012,  19:01)
p.s. в английском не силён, так что чтение мануалов официальных весьма затруднено...

Увы, но вся документация будет только на cpan и только на английском. Вот нужный кусок документации:

When called in void context, nothing is returned. In other contexts, http_request returns a "cancellation guard" - you have to keep the object at least alive until the callback get called. If the object gets destroyed before the callback is called, the request will be cancelled.

Автор: HeinzFelfe 6.12.2012, 18:37
Цитата(DurRandir @ 6.12.2012,  18:13)
If the object gets destroyed before the callback is called, the request will be cancelled.

Можете пояснить о каком объекте идет речь?

Автор: HeinzFelfe 6.12.2012, 19:12
Возможно я не корректно задал вопрос, может пример с конкретным кодом поможет.

прокси должны лежать в файле log.txt

вот для примера список прокси-серверов США:
74.112.203.107:3128
67.208.247.132:3128
96.47.230.49:8080
96.47.230.49:3128
74.221.211.117:8080
198.154.114.100:8080
173.213.113.211:3128
192.211.49.210:3128
198.144.176.54:8080
199.119.76.111:3128
74.112.203.107:8080
74.94.44.210:8080
198.144.176.54:3128
199.119.76.111:8080
74.221.211.117:3128
173.242.117.146:8080
173.213.113.211:8080
173.242.117.146:3128
198.154.114.100:3128
192.211.49.210:8080



Код

#!/usr/bin/perl 
use strict;
use AnyEvent::HTTP;

$AnyEvent::HTTP::MAX_PER_HOST=100;

my $user_agents = [
    'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.4.154.25 Safari/525.19',
    'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; YPC 3.0.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)',
    'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50215) Netscape/8.0.1',
    'Opera/9.50 (Windows NT 5.1; U; ru)',   
];

while (1)
{
    print "\t\tMenu \n\n1. Enter url-address\n2. Quit.\n\nEnter menu item: ";
    my $choise =  <STDIN>; #выбор пункта меню
    if ($choise != 1 && $choise != 2) #проверка на число от 1 до 2
    { 
        print "Wrong number!\n";
        redo;
    } 
    if ($choise == 1)
    {
        printf "Enter url-address with out \'http://www.\': ";
        my $host = <STDIN>;    
        substr($host, -1) = ''; #удаляем перенос строки        
        my @proxy_list = get_ip_port_list();
        my ($res) = check_for_availability ($host, 10, \@proxy_list); #передаем имя хоста к которому будем делать гет запрос и список проксей
        if ($res == 999) { print "ok\n"; }                                            #если check_for_availability вернула 999, то значит получен код 200
        else                  { print "bad\n"; }                                  #в этом случаее код 200 не получен
    }
    if ($choise == 2)
    {
        print "\t\tGood bye...\n\n";
        exit;
    }    
}


sub check_for_availability ()
{    
    my $resource = $_[0];
    my $time_sec = $_[1]; 
    my ($link_arr) = $_[2];
    my @ip_port_list   =  @$link_arr;
    my $cv = AnyEvent->condvar;
    my $schems = 'http';
    my $result;    
    my $i=0;
        
    for (my $j = 0; $j < scalar @ip_port_list; $j++)   #цикл по списку проксей
    {
        my $element = $ip_port_list[$j];
        my @temp = $element =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/g;
        my $host = $temp[0];
        @temp = $element =~ /:\d{1,5}/g;
        my $port = substr($temp[0], 1);
        http_head gen_uri($resource),  proxy => [$host, $port, $schems], timeout => $time_sec, get_params($resource), sub  
        {    
            my ($page , $hr) = @_;
            printf "--- [%-4d:%d]  proxy: %-21s HTTP status code: %-3d\n",  $i+1, scalar @ip_port_list, $element, $hr->{Status};
            if ($hr->{'Status'} == 200 )
            {
                print "200\n";
                $cv->send(999);
            }          
            $i++; 
            if ($i >= scalar @ip_port_list)
            {
                $i=0;
                $cv->send(333);
            }
        };    
    }
    $result = $cv->recv;
    return ($result);
}


sub gen_uri 
{
    my $url = 'http://www.' . $_[0] ;
    return $url;
}

sub get_params
{
    %{{
        cookie_jar => {},
        headers    => {
            'Host'    =>  $_[0],
            'Referer' => 'http://' . $_[0] . '/',
            'User-Agent' => $user_agents->[int rand @$user_agents],
        },
    }};
}

sub get_ip_port_list
{
    my $origfile = 'log.txt';
    #my $origfile = 'all_proxy';
    my %hTmp;
    my @ip_port;
    my $i=0;
    open (IN, "<$origfile") or die "Couldn't open input file: $!";
    while (my $sLine = <IN>) 
    {
        next if $sLine =~ m/^\s*$/; #remove empty lines
        unless ($hTmp{$sLine}++)
        {
            if($sLine =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}/)
            {
                substr($sLine, -1) = '';
                $ip_port[$i] = $sLine;
                $i++;
            }            
        }
    }
    close IN;
    return @ip_port;
}


Если запустить скрипт первый раз, и задать какой-нибудь хост, ya.ru, например, то все отрабоатет нормально, но если после этого снова задать какой-нибудь хост, то сначала будут выведены результыта из прошлого запуска. Что бы было понятней вот пример запуска:

Код

user@userPC:~$ ./cin
        Menu 

1. Enter url-address
2. Quit.

Enter menu item: 1
Enter url-address with out 'http://www.': google.com
--- [1   :20]  proxy: 74.94.44.210:8080     HTTP status code: 407
--- [2   :20]  proxy: 96.47.230.49:8080     HTTP status code: 200
200
ok
        Menu 

1. Enter url-address
2. Quit.

Enter menu item: 1
Enter url-address with out 'http://www.': google.com
--- [3   :20]  proxy: 67.208.247.132:3128   HTTP status code: 200
200
--- [4   :20]  proxy: 96.47.230.49:3128     HTTP status code: 200
200
--- [5   :20]  proxy: 173.213.113.211:3128  HTTP status code: 200
200
--- [6   :20]  proxy: 173.213.113.211:8080  HTTP status code: 200
200
--- [1   :20]  proxy: 67.208.247.132:3128   HTTP status code: 407
--- [2   :20]  proxy: 74.112.203.107:3128   HTTP status code: 200
200
ok
        Menu 

1. Enter url-address
2. Quit.

Enter menu item: 



т.е. в первом запуске он получил ответ 200 для некоторого прокси-адреса (от которого быстрее ответ пришёл) а остальные 18 штук выведины не были:
Код

Enter url-address with out 'http://www.': google.com
--- [1   :20]  proxy: 74.94.44.210:8080     HTTP status code: 407
--- [2   :20]  proxy: 96.47.230.49:8080     HTTP status code: 200
200
ok

И при повторном вводе google.com, он сначала выводит 4 результата (начиная с 3 и до 6)  (видимо до прихода статуса 200 успел сделать всего 6 запрсов) из прошлого вызова, и только оптом начинает выводит результаты для нового запроса:
Код

Enter url-address with out 'http://www.': google.com
--- [3   :20]  proxy: 67.208.247.132:3128   HTTP status code: 200
200
--- [4   :20]  proxy: 96.47.230.49:3128     HTTP status code: 200
200
--- [5   :20]  proxy: 173.213.113.211:3128  HTTP status code: 200
200
--- [6   :20]  proxy: 173.213.113.211:8080  HTTP status code: 200
200
--- [1   :20]  proxy: 67.208.247.132:3128   HTTP status code: 407
--- [2   :20]  proxy: 74.112.203.107:3128   HTTP status code: 200
200
ok
        Menu 


Добавлено через 7 минут и 24 секунды
Вот как мне сбросить остальные запросы, которые послались параллельно с запросом от котрого получен статус 200?

Автор: HeinzFelfe 6.12.2012, 19:30
Так, что-то начинает прояснятся в голове.
Получается, что вот эти запросы из первого запуска, они уже выполнены, т.е.  что успело запросится и вернуться ответ, то и выводится ни каких лишних запросов из первого вызова не выполняется.
Осталось понять как их можно заставить не выводиться.

Автор: DurRandir 7.12.2012, 01:14
Что-то в духе
Код

sub check_for_availability ()
{    
    my $resource = $_[0];
    my $time_sec = $_[1]; 
    my ($link_arr) = $_[2];
    my @ip_port_list   =  @$link_arr;
    my $cv = AnyEvent->condvar;
    my $schems = 'http';
    my $result;    
    my $i=0;
    my @guards;
        
    for (my $j = 0; $j < scalar @ip_port_list; $j++)   #цикл по списку проксей
    {
        my $element = $ip_port_list[$j];
        my @temp = $element =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/g;
        my $host = $temp[0];
        @temp = $element =~ /:\d{1,5}/g;
        my $port = substr($temp[0], 1);
        push @guards, http_head gen_uri($resource),  proxy => [$host, $port, $schems], timeout => $time_sec, get_params($resource), sub  
        {    
            my ($page , $hr) = @_;
            printf "--- [%-4d:%d]  proxy: %-21s HTTP status code: %-3d\n",  $i+1, scalar @ip_port_list, $element, $hr->{Status};
            if ($hr->{'Status'} == 200 )
            {
                print "200\n";
                $cv->send(999);
            }          
            $i++; 
            if ($i >= scalar @ip_port_list)
            {
                $i=0;
                $cv->send(333);
            }
        };    
    }
    $result = $cv->recv;
    undef $_ for @guards;
    return ($result);
}


PS: объект - это тот, который вернёт вызов http_*
PPS: Подумав ещё. Не очень красивый код для определения условия "всё пропало". Посмотрите кусок документации http://search.cpan.org/~mlehmann/AnyEvent-7.04/lib/AnyEvent.pm#METHODS_FOR_PRODUCERS, в части $cv->begin, про AND/OR.

Автор: HeinzFelfe 7.12.2012, 02:40
Цитата(DurRandir @ 7.12.2012,  01:14)
PS: объект - это тот, который вернёт вызов http_*
PPS: Подумав ещё. Не очень красивый код для определения условия "всё пропало". 

Да, за код извиняюсь, писал на скорую руку, для демонстрации наглядной.
Спасибо за ответы и ссылки (и за места где именно копать в них)).

Кстати, в приведенном вами коде появляется странная особенность.
Покажу на примере:
Запускаем скрипт
Код

user@userPC:~$ ./cin

____________________________________________
        Menu 

1. Enter url-address
2. Quit.

Enter menu item: 1

Enter url-address with out 'http://www.': ya.ru
--- Time out for http request: 10

--- [1   :5]  proxy: 85.15.203.1:8080      HTTP status code: 200 
200
ok
____________________________________________
        Menu 

1. Enter url-address
2. Quit.

Enter menu item: 
 

Если подождать, и не вводить сразу данные для следующей итерации, а ввести их секунд через 10, то вылезет вот такая штука, причем в один момент, без задержек

Код

Enter menu item: 1

Enter url-address with out 'http://www.': ya.ru
--- Time out for http request: 10

--- [1   :5]  proxy: 85.15.203.1:8080      HTTP status code: 596 
--- [2   :5]  proxy: 77.38.171.87:8080     HTTP status code: 595 
--- [3   :5]  proxy: 80.90.27.60:8080      HTTP status code: 595 
--- [4   :5]  proxy: 80.90.12.36:8080      HTTP status code: 595 
--- [5   :5]  proxy: 83.136.137.129:8080   HTTP status code: 595 
bad
____________________________________________
        Menu 

1. Enter url-address
2. Quit.

Enter menu item: 


Причем есть прямая зависимость от величены таймаута
Код

my ($res) = check_for_availability ($host, 10, \@proxy_list); 


Чем он меньше, тем меньше нужно жадть. 
Т.е. получается, в то время как программа ожидает ввод данных (новго адреса) где-то на фоне тикает тайм-аут, который в итоге может полностью истеч.
Как такое может быть? И как с этим бороться?


p.s. время ожидания, что бы вывелся на экран такой фокус, вроде как полностью совпадает с заданным тайм-аутом.
p.p.s. проблема с неостанавливающимся таймером была и раньше как выяснилось. Т.е. и до введения переменной @guards и её очистки.
Но как тогда задавать тайм-аут? Как добиться прекращения его истечения уже после выхода из колбэка... 

Автор: HeinzFelfe 7.12.2012, 19:27
новые подробности.
Если изменить значение $AnyEvent::HTTP::MAX_PER_HOST=100; на стандартное 4
то результат 595 будет возвращаться только для первых 4 адресов.


Т.е. код 595 приходит только для первой партии запросов и если $AnyEvent::HTTP::MAX_PER_HOST=100 то для первой сотни всегда будет приходит значение 595.

Как с этим бороться по прежнему неясно.

Автор: HeinzFelfe 7.12.2012, 20:13
Расследование продолжается.
При убирании из запроса информации о прокси, ошибка пропадает.
Все дело в них, проксюшах, но вот в чем конкретно не ясно.
595 - errors during connection etsbalishment, proxy handshake - мало о чем мне говорит.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)