November 2010 Archives

New Ubic slides

Here are the slides from my recent talk at Moscow.pm meeting.
They are more detailed and hopefully less cryptic than my inital presentation.

Ubic - multiservices

(This is a long time overdue post in Ubic series. Previous posts can be found here.)

Last time I explained how any kind of service start/stop/status behavior can be encapsulated in an Ubic::Service subclass. But what if you have many similar services, and want to generate the service list dynamically?

For example, sometimes you already have a directory with daemon configs, and want to run them all without creating additional "service description" file per daemon.
Or you may wish to start ten instances of one processing script.
Or maybe you keep the list of your services in SQL database on another host.

In all these cases, multiservices come to the rescue.
Multiservice is a container object which provides part of the service tree.

For example, let's assume you have a dir /usr/share/psgi-apps/ with some psgi apps in it:


$ ls /usr/share/psgi-apps/
hello-14001.psgi world-14002.psgi

Future HTTP port number in this example is encoded in the file name.

Let's map these apps into ubic service tree:


# content of /etc/ubic/service/psgi-apps file

use parent qw(Ubic::Multiservice);
use Ubic::Service::Plack;

sub new {
return bless {} => shift;
}

sub service_names {
my @files = glob "/usr/share/psgi-apps/*.psgi";
s{^/usr/share/psgi-apps/(.*)-\d+.psgi$}{$1} for @files;
return @files;
}

sub simple_service {
my $name = $_[1];
my ($file) = glob "/usr/share/psgi-apps/$name-*.psgi";
my ($port) = $file =~ /(\d+)\.psgi$/;
return Ubic::Service::Plack->new({
server => 'Standalone',
app => $file,
app_name => $name,
port => $port,
stdout => "/var/log/psgi-apps/$name.log",
stderr => "/var/log/psgi-apps/$name.err.log",
});
}

return __PACKAGE__->new;

Assuming that /var/log/psgi-apps/ dir exists, this code is enough to run existing *and* all future psgi apps in /usr/share/psgi-apps/ dir.
"service_names" method returns plain list of service names, and "simple_service" builds service by name. It's really simple :)

Now, all psgi apps can be started simultaneously with one command:


$ sudo ubic start -f psgi-apps
psgi-apps
Starting psgi-apps.hello... started
Starting psgi-apps.world... started

... or one by one:


$ sudo ubic stop psgi-apps.world
Stopping psgi-apps.world... stopped

When someone is performing some global action, multiservices are grouped in indented trees:


$ sudo ubic status
ubic-ping running (pid 18641)
sleeping-daemon running (pid 8056)
psgi-apps
psgi-apps.hello running
psgi-apps.world off

And of course you can create several levels of nesting too if you need them.


There are two multiservice classes in core Ubic distribution.

First, there is a Ubic::Multiservice::Simple class, which can be used for cases when you just want to group several services together:


# content of /etc/ubic/service/my
use Ubic::Multiservice::Simple;
...
return Ubic::Multiservice::Simple->new({
fcgi_app => $fcgi_app_service,
memcached => $memcached_service,
});

It's also handy for generating several identical workers:


# content of /etc/ubic/service/ten-workers
use Ubic::Multiservice::Simple;
use Ubic::Service::SimpleDaemon;
return Ubic::Multiservice::Simple->new({
map {(
"worker$_" => Ubic::Service::SimpleDaemon->new({
bin => "worker.pl",
})
)} (1..10)
});

Second, there is a Ubic::Multiservice::Dir. Nobody usually uses it directly, but it is constructed for every subdir in /etc/ubic/service/, so that /etc/ubic/service/a/b file is mapped to "a.b" service.

So, summing it up, multiservices are useful for:
- grouping services together for easier management;
- dynamically generating services from external sources;
- running a large number of identical worker processes.
They really simplify some complex tasks and make ubic as unobtrusive and extensible as it is possible.

PS: PSGI example requires Ubic=1.22 and Ubic::Service::Plack=1.12. I made multiservice API slightly simpler while writing this post, and fixed one bug in Ubic::Service::Plack. Both of these modules should be available on CPAN soon.

PPS: Contacts:
our irc channel is #ubic at irc.perl.org;
source code is at http://github.com/berekuk/ubic;
mailing list is at http://groups.google.com/group/ubic-perl.

If you are using ubic or have any questions, let me know. It motivates me SO much :)

About Vyacheslav Matyukhin

user-pic I blog about Perl.