【问题标题】:Perl divide hash into equal parts based on count and send for Parallel executionPerl 根据计数将哈希分成相等的部分并发送给并行执行
【发布时间】:2025-12-01 07:25:01
【问题描述】:

我有一个哈希(%hash),其中包含节点列表和需要为各个节点执行的命令。

在此之前,我有主机列表(@alive_hosts)应该在哪个主机上执行。

这是我的代码:

#!/usr/bin/perl

use strict;
use warnings;

use Data::Dumper;

my @alive_hosts = qw/10.0.0.1 10.0.0.2/;
print Dumper(\@alive_hosts);

my %hash = (
          'Node1' => 'cmd1 | cmd2 | cmd3',
          'Node2' => 'cmd2 | cmd3',
          'Node3' => 'cmd4 | cmd1',
          'Node4' => 'cmd1',
          'Node5' => 'cmd2',
          'Node6' => 'cmd1 | cmd2',
          'Node7' => 'cmd3 | cmd4',
);
print Dumper(\%hash);

my $num_buckets = scalar @alive_hosts;
print "num_buckets:$num_buckets\n"; 

my $no_of_nodes = scalar keys %hash;

my $per_bucket  = int( $no_of_nodes / $num_buckets ); 
print "per_bucket:$per_bucket\n";

my $num_extras  =      $no_of_nodes % $num_buckets; 
print "num_extras:$num_extras\n";

我想以这样一种方式划分这个哈希(%hash),即根据活动主机的数量,哈希应该被划分。以便将其分发给每个主机。 在上面的例子中, Host1(10.0.0.1) 应包含:

'Node1' => 'cmd1 | cmd2 | cmd3',
'Node2' => 'cmd2 | cmd3',
'Node3' => 'cmd4 | cmd1',
'Node4' => 'cmd1'

Host2(10.0.0.2) 应包含:

'Node5' => 'cmd2',
'Node6' => 'cmd1 | cmd2',
'Node7' => 'cmd3 | cmd4'

以上 2 个值可以保存在新的哈希中,从那里我需要执行一个 shell 脚本,将上述值(即节点和 cmds)作为并行参数传递。为了并行执行此操作,我想使用Parallel::LoopsParallel::ForkManager。任何想法/建议将不胜感激。

【问题讨论】:

    标签: perl hash parallel-processing fork


    【解决方案1】:

    看看你是否认为下一个方法可以接受

    use strict;
    use warnings;
    use feature 'say';
    
    use Data::Dumper;
    
    my @alive_hosts = qw/10.0.0.1 10.0.0.2/;
    print Dumper(\@alive_hosts);
    
    my %hash = (
              'Node1' => 'cmd1 | cmd2 | cmd3',
              'Node2' => 'cmd2 | cmd3',
              'Node3' => 'cmd4 | cmd1',
              'Node4' => 'cmd1',
              'Node5' => 'cmd2',
              'Node6' => 'cmd1 | cmd2',
              'Node7' => 'cmd3 | cmd4',
    );
    print Dumper(\%hash);
    
    my %dispatch;
    my @hosts;
    
    while( my($node,$cmd) = each %hash ) {
        @hosts = @alive_hosts unless @hosts;
        my $host = shift @hosts;
        $dispatch{$host}{$node} = $cmd;
    
    }
    
    say Dumper(\%dispatch);
    

    输出

    $VAR1 = [
              '10.0.0.1',
              '10.0.0.2'
            ];
    $VAR1 = {
              'Node1' => 'cmd1 | cmd2 | cmd3',
              'Node4' => 'cmd1',
              'Node6' => 'cmd1 | cmd2',
              'Node5' => 'cmd2',
              'Node3' => 'cmd4 | cmd1',
              'Node7' => 'cmd3 | cmd4',
              'Node2' => 'cmd2 | cmd3'
            };
    $VAR1 = {
              '10.0.0.1' => {
                              'Node6' => 'cmd1 | cmd2',
                              'Node1' => 'cmd1 | cmd2 | cmd3',
                              'Node3' => 'cmd4 | cmd1',
                              'Node2' => 'cmd2 | cmd3'
                            },
              '10.0.0.2' => {
                              'Node4' => 'cmd1',
                              'Node5' => 'cmd2',
                              'Node7' => 'cmd3 | cmd4'
                            }
            };
    

    【讨论】:

      【解决方案2】:

      您已经计算出在每个新哈希中需要多少个节点。因此,您可以从大哈希中获取密钥列表,并且每次循环时只需 slice() 该数字即可。

      类似这样的:

      #!/usr/bin/perl
      
      use strict;
      use warnings;
      use 5.20; # For the new hash slices
      use feature 'say';
      
      use Data::Dumper;
      
      my @alive_hosts = qw/10.0.0.1 10.0.0.2/;
      print Dumper(\@alive_hosts);
      
      my %hash = (
                'Node1' => 'cmd1 | cmd2 | cmd3',
                'Node2' => 'cmd2 | cmd3',
                'Node3' => 'cmd4 | cmd1',
                'Node4' => 'cmd1',
                'Node5' => 'cmd2',
                'Node6' => 'cmd1 | cmd2',
                'Node7' => 'cmd3 | cmd4',
      );
      print Dumper(\%hash);
      
      my $no_of_nodes = scalar keys %hash;
      my $num_buckets = scalar @alive_hosts;
      
      my $per_bucket  = int( $no_of_nodes / $num_buckets );
      $per_bucket++ if $no_of_nodes % $num_buckets;
      
      my @keys = keys %hash;
      
      my %node_hash;
      
      for (1 .. $num_buckets) {
        my @newkeys = splice @keys, 0, $per_bucket;
      
        $node_hash{$alive_hosts[$_ - 1]} = { %hash{@newkeys} }; # New hash slice syntax
      }
      
      say Dumper \%node_hash;
      

      注意:我使用 new(ish)(自 Perl 5.20 起)%hash{...} 哈希切片语法。如果您使用的是早期版本的 Perl,则需要调整该行。

      【讨论】:

      • 谢谢@dave。这解决了创建新哈希的问题。我已经更新了如何让这个值在并行处理中执行的问题。
      • @vinodk89:如果您还有其他问题,请不要在此处添加,而是在网站上发布新问题。