App-Ringleader

 view release on metacpan or  search on metacpan

lib/App/Ringleader.pm  view on Meta::CPAN

has inactivity_delay => (
    is => 'ro',
    isa => 'Int',
    lazy => 1,
    default => sub {
        60 * ( $_[0]->configuration->{inactivity_delay} || 60 );
    },
);

has services => (
    is => 'ro',
    lazy => 1,
    default => sub {
        my $services = $_[0]->configuration->{services};

        $_ = Ubic->service($_) for values %$services;

        return $services;
    },
);

has cache => (
    is => 'ro',
    lazy => 1,
    default => sub {
        return CHI->new(
            %{ $_[0]->configuration->{CHI} 
                || { driver => 'FastMmap' } }
        );
    },
);

sub run {
    my $self = shift;

    my $proxy = HTTP::Proxy->new( host => undef, port => $self->port );

    $proxy->push_filter( 
        request => HTTP::Proxy::HeaderFilter::simple->new( sub {
            my( undef, undef, $request ) = @_;
            my $uri = $request->uri;
            my $host = $uri->host;

            my $service = $self->services->{ $host } or die;

            $uri->host( 'localhost' );
            $uri->port( $service->port );

            unless ( $self->cache->get($host) ) {
                $service->start;
                sleep 1;
            }

            # always store the latest access time
            $self->cache->set( $host => time );
        })
    );

    $self->start_monitor;

    say 'ringleader started...';

    $proxy->start;
}

sub start_monitor {
    my $self = shift;

    return if fork;

    while( sleep $self->inactivity_delay ) {
        $self->check_activity_for( $_ ) for keys %{ $self->services };
    }
}

sub check_activity_for {
    my( $self, $s ) = @_;

    my $time = $self->cache->get($s);

    # no cache? assume not running
    return if !$time or time - $time <= $self->inactivity_delay;

    $self->services->{$s}->stop;

    $self->cache->remove($s);
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

App::Ringleader - Proxy for sproradically-used web application

=head1 VERSION

version 0.1.0

=head1 SYNOPSIS

    use App::Ringleader;

    App::Ringleader->new( conf => 'ringleader.yml' )->run;

=head1 DESCRIPTION

Ringleader is a proxy that will wake up psgi applications upon request 
and shut them down after a period of inactivity. It's meant to provide a
middle-ground between the slowliness of CGI and the constant resource
consumption of plack applications for services that are not often used.

Typically, you'll use it via the C<ringleader> script.

Ringleader relies on L<Ubic> to start and stop the services. For PSGI
applications, you probably want to define your services using



( run in 0.432 second using v1.01-cache-2.11-cpan-d7a12ab2c7f )