首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >Net::LDAP和并行::Forkmanager可以一起工作吗?

Net::LDAP和并行::Forkmanager可以一起工作吗?
EN

Stack Overflow用户
提问于 2015-03-06 06:42:19
回答 1查看 126关注 0票数 0

我需要通过Net::LDAP查询perl中的不同LDAP服务器。我有些东西很好用。然而,为了加快速度,我尝试使用并行::Forkmanager并行地查询不同的服务器--当我这样做时,事情就不能工作了。

我得到以下类型的错误:

代码语言:javascript
代码运行次数:0
运行
复制
 decode error 02<=>30 0 8  at /Users/myname/perl5/perlbrew/perls/perl-5.18.2/lib/site_perl/5.18.2/Convert/ASN1/_decode.pm line 113, <> line 18.
 decode error 43<=>30 0 8  at /Users/myname/perl5/perlbrew/perls/perl-5.18.2/lib/site_perl/5.18.2/Convert/ASN1/_decode.pm line 113, <> line 25.

在获得搜索响应的行:

代码语言:javascript
代码运行次数:0
运行
复制
$mesg = $ldap->search( base => $dn, filter => '(CN=*)');

我很困惑。

换句话说,为什么这样做失败了:

代码语言:javascript
代码运行次数:0
运行
复制
use Net::LDAP;
use Parallel::Forkmanager;

...; # bind LDAP servers

while (<>) {
    chop;
    my $dn = $_;
    foreach my $ldap (@servers) {
        my $pid;
        $pid = $pm->start and next; # do the fork
        print $dn, $pid;
        my $mesg;
        try {
            $mesg = $ldap->search( base => $dn, filter => '(CN=*)');
        } catch {
            ...;
        }
        $pm->finish;
    }
}

而这一点:

代码语言:javascript
代码运行次数:0
运行
复制
use Net::LDAP;

...; # bind LDAP servers

while (<>) {
    chop;
    my $dn = $_;
    foreach my $ldap (@servers) {
        print $dn;
        my $mesg;
        try {
            $mesg = $ldap->search( base => $dn, filter => '(CN=*)');
        } catch {
            ...;
        }
    }
}

效果很好吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-03-06 09:40:47

虽然forking没有完全相同的线程安全问题,但threading仍然有一些地方。我认为这就是让您头疼的地方--您的Net::LDAP对象是在父线程中创建的,但是当您分叉时(实际上)将它们克隆到每个线程。

这意味着在您的代码中--如果您有足够快的名称列表,那么一个新的分叉将尝试重用一个现有的Net::LDAP连接,然后再完成上一个连接。

防止这种情况的简单方法是调用wait_all_children,以确保在下一个查询开始之前完成所有并行的LDAP查询。

如果将LDAP绑定放在ForkManager循环中,那么仍然存在相同的问题吗?我理解这是一种潜在的开销,因为您将在每次迭代中绑定,但是如果这解决了这个问题,我建议这是因为Net::LDAP在叉子之间共享相同的文件描述符。

下一个最好的解决方案是采用“worker”模型,其中有一群“工人”,每个人都有他们在LDAP上的连接来进行查询。与forking相比,线程处理要容易得多:

代码语言:javascript
代码运行次数:0
运行
复制
#!/usr/bin/perl

use strict;
use warnings;

use threads;
use Thread::Queue;

sub server_worker {
    my ( $hostname, $q_ref ) = @_;

    ## connect LDAP to $hostname;
    while ( my $dn = $$q_ref->dequeue ) {

        #query $dn
    }
}

my @queues;

foreach my $server (@list_of_servers) {
    my $server_q = Threads::Queue->new();
    push( @queues, $server_q );
    threads->create( \&server_worker, $hostname, \$server_q );
}

while ( my $dn = <STDIN> ) {
    chomp($dn);
    foreach my $q (@queues) {
        $q->enqueue($dn);
    }
}

foreach my $q ( @queues ) { 
    $q -> end;
}

foreach my $thr ( threads->list ) {
    $thr->join();
}

用叉子做类似的事情应该有效:

代码语言:javascript
代码运行次数:0
运行
复制
#!/usr/bin/perl

use strict;
use warnings;

use IO::Pipe;
use Parallel::ForkManager;
use Net::LDAP;

my @list_of_servers = qw ( servername servenama anotherserver );

my $pm = Parallel::ForkManager -> new ( scalar @list_of_servers );

my %pipe_for;

foreach my $server ( @list_of_servers ) {
   my $pipe = IO::Pipe -> new();
   my $pid = pm -> start;
   if ( $pid ) {
       print "$$: parent\n";
       $pipe -> writer -> autoflush;
       $pipe_for{$server} = $pipe;
   }
   else {
       print "$$ child connecting to $server\n"; 
       $pipe -> reader -> autoflush;
       close ( STDIN ); #because this is a child. 
        #Net::LDAP setup
       while ( my $item =  <$pipe> ) {
           chomp ( $item );
            #ldap_search $item;
       }
    }
   $pm -> finish; 
}

然后发送东西:

代码语言:javascript
代码运行次数:0
运行
复制
for my $number ( 1..10 ) { 
   foreach my $pipe ( values %pipe_for ) { 
       print {$pipe} "test$number\n";
   }
}

$pm -> wait_all_children();

编辑:注意--自动刷新很重要,否则IO缓冲区就不工作了。我非常肯定,关闭STDIN对孩子来说可能是个好主意,但如果他们不使用STDIN的话,也许并不是绝对必要的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28893550

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档