Beekeeper
view release on metacpan or search on metacpan
lib/Beekeeper/Service/Supervisor/Worker.pm view on Meta::CPAN
package Beekeeper::Service::Supervisor::Worker;
use strict;
use warnings;
our $VERSION = '0.10';
use Beekeeper::Worker ':log';
use base 'Beekeeper::Worker';
use Beekeeper::Worker::Extension::SharedCache;
our $CHECK_PERIOD = $Beekeeper::Worker::REPORT_STATUS_PERIOD;
sub authorize_request {
my ($self, $req) = @_;
if ($req->{method} eq '_bkpr.supervisor.worker_status' ||
$req->{method} eq '_bkpr.supervisor.worker_exit' ) {
return unless $self->__has_authorization_token('BKPR_SYSTEM');
}
else {
return unless $self->__has_authorization_token('BKPR_ADMIN');
}
return BKPR_REQUEST_AUTHORIZED;
}
sub on_startup {
my $self = shift;
$self->{host} = $self->{_WORKER}->{hostname};
$self->{pool} = $self->{_WORKER}->{pool_id};
$self->{Workers} = $self->shared_cache( id => "workers", max_age => $CHECK_PERIOD * 4 );
$self->{Queues} = {};
$self->{Load} = {};
$self->accept_notifications(
'_bkpr.supervisor.restart_pool' => 'restart_pool',
'_bkpr.supervisor.restart_workers' => 'restart_workers',
);
$self->accept_remote_calls(
'_bkpr.supervisor.worker_status' => 'worker_status',
'_bkpr.supervisor.worker_exit' => 'worker_exit',
'_bkpr.supervisor.get_workers_status' => 'get_workers_status',
'_bkpr.supervisor.get_services_status' => 'get_services_status',
);
$self->{check_status_tmr} = AnyEvent->timer(
after => rand($CHECK_PERIOD),
interval => $CHECK_PERIOD,
cb => sub {
$self->check_workers;
$self->check_queues;
},
);
}
sub on_shutdown {
my $self = shift;
# Disconnect shared cache
undef $self->{Workers};
}
sub log_handler {
my $self = shift;
# Use pool's logfile
$self->SUPER::log_handler( foreground => 1 );
}
sub worker_status {
my ($self, $params) = @_;
$self->set_worker_status( %$params );
}
sub worker_exit {
my ($self, $params) = @_;
$self->remove_worker_status( %$params );
# Check for unserviced queues, just in case of worker being the last of its kind
$self->check_queues;
}
sub set_worker_status {
my ($self, %args) = @_;
my $pool = $args{'pool'} || die;
my $host = $args{'host'} || die;
my $pid = $args{'pid'} || die;
my $worker_id = "$host:$pool:$pid";
my $status = $self->{Workers}->get( $worker_id ) || {};
$status = { %$status, %args };
$self->{Workers}->set( $worker_id => $status );
if ($status->{queue}) {
$self->{Queues}->{$_} = 1 foreach @{$status->{queue}};
}
}
sub touch_worker_status {
my ($self, %args) = @_;
lib/Beekeeper/Service/Supervisor/Worker.pm view on Meta::CPAN
$services{$worker->{class}}{nps} += $worker->{nps};
$services{$worker->{class}}{err} += $worker->{err};
$services{$worker->{class}}{cpu} += $worker->{cpu} || 0;
$services{$worker->{class}}{mem} += $worker->{mem} || 0;
$services{$worker->{class}}{load} += $worker->{load};
}
foreach my $service (values %services) {
$service->{load} = $service->{load} / $service->{count};
}
foreach my $service (values %services) {
$service->{cps} = sprintf("%.2f", $service->{cps} );
$service->{nps} = sprintf("%.2f", $service->{nps} );
$service->{err} = sprintf("%.2f", $service->{err} );
$service->{cpu} = sprintf("%.2f", $service->{cpu} );
$service->{mem} = sprintf("%.2f", $service->{mem} );
$service->{load} = sprintf("%.2f", $service->{load});
}
return \%services;
}
sub restart_workers {
my ($self, $args) = @_;
return if ($args->{host} && $args->{host} ne $self->{host});
return if ($args->{pool} && $args->{pool} ne $self->{pool});
my $workers = $self->_get_workers(
host => $self->{host},
pool => $self->{pool},
class => $args->{class},
);
log_info "Restarting workers" . ($args->{class} ? " $args->{class}..." : "...");
my @worker_pids;
foreach my $worker (@$workers) {
# Do not restart supervisor
next if ($worker->{class} eq 'Beekeeper::Service::Supervisor::Worker');
my ($pid) = ($worker->{pid} =~ m/^(\d+)$/); # untaint
push @worker_pids, $pid if ($pid);
}
if (!$args->{delay}) {
# Restart all workers at once
foreach my $pid (@worker_pids) {
kill( 'TERM', $pid );
}
}
else {
# Slowly restart all workers
my $delay = $args->{delay};
my $count = 0;
foreach my $pid (@worker_pids) {
$self->{restart_worker_tmr}->{$pid} = AnyEvent->timer(
after => $delay * $count++,
cb => sub {
delete $self->{restart_worker_tmr}->{$pid};
kill( 'TERM', $pid );
},
);
}
}
}
sub restart_pool {
my ($self, $args) = @_;
return if ($args->{host} && $args->{host} ne $self->{host});
return if ($args->{pool} && $args->{pool} ne $self->{pool});
my $wpool_pid = $self->{_WORKER}->{parent_pid};
my $delay = $args->{delay};
if (!$delay) {
kill( 'HUP', $wpool_pid );
}
else {
my $index = $self->_get_pool_index( $self->{host}, $self->{pool} );
$self->{restart_pool_tmr} = AnyEvent->timer(
after => $delay * $index,
cb => sub {
delete $self->{restart_pool_tmr};
kill( 'HUP', $wpool_pid );
},
);
}
}
sub _get_pool_index {
my ($self, $host, $pool) = @_;
# Sort all pools by name, then return the index of the requested one.
# Used by restart_pool() to determine restart order across hosts
my %pools;
foreach my $worker (values %{$self->{Workers}->{data}}) {
next unless defined $worker;
$pools{"$worker->{host}:$worker->{pool}"} = 1;
}
return 0 unless $pools{"$host:$pool"};
my $index = 0;
foreach my $key (sort keys %pools) {
last if ($key eq "$host:$pool");
$index++;
}
return $index;
}
1;
__END__
=pod
=encoding utf8
=head1 NAME
Beekeeper::Service::Supervisor::Worker - Worker pool supervisor
=head1 VERSION
Version 0.09
=head1 DESCRIPTION
A Supervisor worker is created automatically in every worker pool.
It keeps a shared table of the performance metrics of every worker connected to
every broker, and routinely measures the CPU and memory usage of local workers.
These metrics can be queried using the methods provided by L<Beekeeper::Service::Supervisor>
or using the command line client L<bkpr-top>.
( run in 1.693 second using v1.01-cache-2.11-cpan-0b5f733616e )