Sorry for later.

http://perlchina.org/advent/2009/ParallelSig.html

=for advent_year 2009

=for advent_day 18

=for advent_title Parallel::Prefork 和 SIG

=for advent_author Fayland Lam

如果一个 daemon 处理队列太慢(或其他情况),M<Parallel::ForkManager> 和
M<Parallel::Prefork> 都是很不错的选择。

下面的代码来自我们实际运行中的一个 M<TheSchwartz> worker 的 daemon, 实际上很大一部分都是拷贝自
A<http://d.hatena.ne.jp/tokuhirom/20081110/1226291955>

=begin codeNNN

use strict;
use warnings;

my $has_proc_pid_file
    = eval 'use Proc::PID::File; 1;';    ## no critic (ProhibitStringyEval)
my $has_home_dir
    = eval 'use File::HomeDir; 1;';      ## no critic (ProhibitStringyEval)
if ( $has_proc_pid_file and $has_home_dir ) {
    # If already running, then exit
    if ( Proc::PID::File->running( { dir => File::HomeDir->my_home } ) ) {
        exit(0);
    }
}

use UNIVERSAL::require;
use Parallel::Prefork;

my @workers = qw/
    TheSchwartz::JobA
    TheSchwartz::JobB
    TheSchwartz::JobC
/;
foreach my $worker (@workers) {
    print "setup $worker\n";
    $worker->use or die $@;
}

sub MaxRequestsPerChild () { 2 }

print "start prefork\n";
my $pm = Parallel::Prefork->new({
    max_workers  => 3,
    fork_delay   => 1,
    trap_signals => {
        TERM => 'TERM',
        HUP  => 'TERM',
    },
});

while ($pm->signal_received ne 'TERM') {
    $pm->start and next;
    print "spawn $$\n";

    my $client = TheSchwartz::Moosified->new( databases => [$dbh] );
    $client->can_do($_) foreach @wokers;

    my $reqs_before_exit = MaxRequestsPerChild;
    $SIG{TERM} = sub { $reqs_before_exit = 0 };
    while ($reqs_before_exit > 0) {
        if ($client->work_once) {
            print "work $$\n";
            --$reqs_before_exit;
        } else {
            sleep 10;
        }
    }

    print "FINISHED $$\n";
    $pm->finish;
}

$pm->wait_all_children;

die "HMM????";

=end codeNNN

代码是自解释的。:)

但是有时候我们需要停止它的时候(它由 crontab 启动),根据代码我们需要发送 TERM 给脚本,然后脚本才会优雅地退出。这时候
M<Proc::ProcessTable> 就派上大用场了。

=begin codeNNN

use Proc::ProcessTable;

my $p = new Proc::ProcessTable( 'cache_ttys' => 1 );
my $all = $p->table;
foreach my $one (@$all) {
    if ($one->cmndline =~ /TheSchwartz/) {
        next if ( $one->cmndline =~ /TheSchwartz_restart/ ); # itself
        my $pid = $one->pid;
        print "kill -15 $pid\n";
        `kill -15 $pid`; # send TERM
    }
}

=end codeNNN

Proc::ProcessTable 类似于 ps, 我们找到 pid 后,发送 TERM 过去就大功告成了。

谢谢。

-- 
Fayland Lam // http://www.fayland.org/

--

您收到此邮件是因为您订阅了 Google 网上论坛的“PerlChina Mongers 讨论组”论坛。
要向此网上论坛发帖,请发送电子邮件至 [email protected]。
要取消订阅此网上论坛,请发送电子邮件至 [email protected]。
若有更多问题,请通过 http://groups.google.com/group/perlchina?hl=zh-CN 访问此网上论坛。


回复