[Rio-pm] Ajuda com Threads - tempo de abertura

João André Simioni jasimioni em gmail.com
Sábado Julho 10 08:35:43 PDT 2010


Blabos,

o loop interno não é nem usado nesse código, como observado pelo
Bruno, porque ele define a quantidade de threads no início. Usualmente
eu disparo uma thread para cada elemento que vou gerenciar, e defino
um limite de threads abertas no início do código e é por isso que esse
loop existe, para controlar o limite de threads (e funciona bem em
diversos outros scripts que não têm o mesmo problema de desempenho).

Segue os comentários no código:

> #!/usr/bin/perl
>
> use strict;
> use DBI;
> use Net::Ping;
> use Time::HiRes qw/usleep tv_interval gettimeofday/;
> use threads ('yield', 'stack_size' => 32*4096, 'exit' =>
> 'threads_only', 'stringify');
>
> my $host = '192.168.160.179';
> my $sid  = 'HOMOLOG';
> my $user = 'mac_user';
> my $pass = 'mac_user';
>
> my $dbh = DBI->connect("dbi:Oracle:host=$host;sid=$sid", $user, $pass,
> { AutoCommit => 1 });
>
> my $sth = $dbh->prepare('SELECT CPE_ID, CLIENTE_ID, CPE_DESC,
> CPE_IP_WAN, CPE_NODE, CPE_IF FROM MAC_CPE WHERE CPE_PING = ? AND
> CPE_ATIVO = ?');
> $sth->execute('1', 'A');
>
> my @cpes;
>
> while (my (@row) = $sth->fetchrow_array) {
>     push @cpes, [ @row ];
> }
>
> $dbh->disconnect();

Até aqui somente listei os elementos a partir do banco de dados.

O código abaixo quebra o numero de elementos em 250 threads.

> my $maxThreads = 250;
> my $total      = @cpes;
>
> my $cpePerThread = int($total / $maxThreads);
>
> my @toMonitor;
> my $i = 0;
> my $c = 0;
>
> foreach my $cpe (@cpes) {
>     push @{$toMonitor[$i]}, $cpe;
>     $c++;
>
>     if ($c > $cpePerThread) {
>         $i++;
>         $c = 0;
>     }
> }
>
> print "Para $total cpes, e $maxThreads threads, tenho no final $i
> grupos, com ", scalar @{$toMonitor[0]}, " elementos cada\n";



E imprimo - vou ter o total de elementos dividido em 250 grupos (cada
grupo atendido por uma thread)



> sub processResult {
>     my $r = shift;
>     if (ref $r ne 'ARRAY') {
>         print STDERR "Erro na thread\n";
>         return 0;
>     }
>     my ($result, $host, $ifs) = @$r;
>
> }

Funcao de processamento do resultado da thread -- aqui não faz nada

> sub testeClient {
>     my $threadNum = shift;
>     my $cpeGroup  = shift;
>     foreach my $cpe (@{$cpeGroup}) {
>         my ($cpeId, $clientId, $cpeDesc, $cpeIp, $cpeNode, $cpeIf) = @$cpe;
>         my $pingOk = &checkIp($cpeIp);
>         my $status = $pingOk ? 'RESPONDE' : 'MORTO';
>         # print join(", ", $threadNum, $cpeId, $clientId, $cpeDesc,
> $cpeIp, $cpeNode, $cpeIf, $status), "\n";
>     }
>     # print "Finishing thread $threadNum\n";
>     return([1, 0]);
> }

Código da thread - basicamente chama a sub 'checkIp' para ver se o
host está vivo.

Abaixo o código de criação das threads - para cada grupo, cria uma thread.

> my $threadCount = 1;
> foreach my $cpeGroup (@toMonitor) {
>     print "Trying to create thread";
>     my $t0 = [gettimeofday];
>
>     print " in ", scalar localtime $t0->[0], ".", $t0->[1], "\n";
>
>     my $thr = threads->create({scalar => '1'}, 'testeClient',
> $threadCount, $cpeGroup);
>     my $elapsed = tv_interval ( $t0, [gettimeofday]);
>     print "Created thread $thr ($threadCount) - took $elapsed seconds
> to create\n";
>     $threadCount++;

Esse bloco pode ser removido, pois nunca entra - foi a primeira coisa
que tirei, porque achei que a chamada a threads->list poderia estar
influenciando. Esse método retorna em menos de 1ms em todas as
execuções. Mas se preferir só ignora.

>     while (threads->list() >= $maxThreads) {
>         my @joinable = threads->list(threads::joinable);
>         for (@joinable) {
>             my $r = $_->join();
>             &processResult($r);
>         }
>         usleep(10000);
>     }
>     my $elapsed = tv_interval ( $t0, [gettimeofday]);

Finaliza a criação de threads

> }

Aguarda as threads concluirem.

> while (threads->list()) {
>     my @joinable = threads->list(threads::joinable);
>     for (@joinable) {
>         my $r = $_->join();
>         &processResult($r);
>     }
>     usleep(10000);
> }

Fim de execução

> sub checkIp {
>     my $ip = shift;
>     my $pingOk = 0;


Essa rotina faz 3 testes para cada elemento. Em cada teste, tenta um
ping ICMP, um UDP e um TCP. Tenho que executar o ping do S.O. porque
senão sou obrigado a rodar o script como root e o Net::Ping faz
confusão com o ICMP. Se eu testo um host e ele nao responde no tempo
de timeout e eu em seguida testar outro host, se o primeiro responder
ele acha que o segundo respondeu.

>     eval {
>         for (1..3) {
>             my $ping = `/bin/ping -c 1 -w 1 $ip`;
>
>             if (grep { / 0% packet loss/ } $ping) {
>                 $pingOk = 1;
>                 return 1;
>             }
>
>             if ($pingOk == 0) {
>                 my $p = Net::Ping->new('icmp');
>                 $p->service_check(1);
>                 if ($p->ping($ip, 1)) {
>                     $pingOk = 1;
>                     return 1;
>                 }
>             }
>
>             if ($pingOk == 0) {
>                 my $p = Net::Ping->new('udp');
>                 $p->service_check(1);
>                 if ($p->ping($ip, 1)) {
>                     $pingOk = 1;
>                     return 1;
>                 }
>             }
>
>             sleep 3;
>         }
>     };
>
>     return $pingOk;
> }
>

Quanto a usar Java, é que tenho um grupo de desenvolvimento Java do
meu lado - eu posso solicitar a eles, mas eu não vejo porque Perl não
pode atender bem nesse caso.

Obrigado


Mais detalhes sobre a lista de discussão Rio-pm