Brad Fitzpatrick (brad) wrote,
Brad Fitzpatrick
brad

perlbal

I must have slept too much because I'm having so much fun with this load balancer in Perl. I now have a config file, in which I can configure a management port which takes commands of the same type as the config interface:

[Error: Irreparable invalid markup ('<pre [...] '>') in entry. Owner must fix manually. Raw contents below.]

I must have slept too much because I'm having so much fun with this load balancer in Perl. I now have a config file, in which I can configure a management port which takes commands of the same type as the config interface:

<lj-cut text="Wordy tour through fun code..."><pre style='border: 1px solid black''>$ cat perlbal.conf
# comments work
CREATE SERVICE web_proxy # word
SET web_proxy.role = reverse_proxy
SET web_proxy.listen = 10.1.0.10:8080
SET web_proxy.balance_method = sendstats
SET web_proxy.sendstats.listen = 10.1.0.2:5434
ENABLE web_proxy

CREATE SERVICE mgmt
SET mgmt.role = management
SET mgmt.listen = 10.1.0.10:8065
ENABLE mgmt</pre>Now, start it up:
<pre style='border: 1px solid black'>./perlbal.pl --config=./perlbal.conf</pre> (No --daemonize yet)

The service roles can be "reverse_proxy", "management", or in the future "webserver". All the other attributes are tied to the role and other attributes. For instance, you can't set the sendstats.listen attribute of a service if it's not using balance_method = sendstats, and you can't set balance_method if it's not of role reverse_proxy.

So with the above config I can telnet to 10.1.0.10:8065 and create new services while it's running, or even change the listening IP of a running one, without killing active connections.

Implementing the management role was trivial:
<pre style='border: 1px solid black''>package Perlbal::ClientManage;
our @ISA = ("Perlbal::Socket");
# ClientManage
sub new {
my ($class, $service, $sock) = @_;
my $self = $class-&gt;SUPER::new($sock);
$self-&gt;{service} = $service;
$self-&gt;{buf} = ""; # what we've read so far, not forming a complete line
bless $self, ref $class || $class;
$self-&gt;watch_read(1);
return $self;
}

# ClientManage
sub event_read {
my $self = shift;

my $bref = $self-&gt;read(1024);
return $self-&gt;close() unless defined $bref;
$self-&gt;{buf} .= $$bref;

if ($self-&gt;{buf} =~ s/^(.+?)\r?\n//) {
my $line = $1;
Perlbal::run_manage_command($line, sub {
$self-&gt;write("$_[0]\r\n");
});
}
}

# ClientManage
sub event_err { my $self = shift; $self-&gt;close; }
sub event_hup { my $self = shift; $self-&gt;close; }</pre>Notice the closure to run_management_command which sets how the management commands do their output? Also notice I don't define the event_write (called when the socket is writable), instead using the base class implementation. The config file loader uses the same run_manage_command:
<pre style='border: 1px solid black''>sub load_config {
my ($file, $writer) = @_;
open (F, $file) or die "Error opening config file ($file): $!\n";
while (&lt;F&gt;) {
return 0 unless run_manage_command($_, $writer);
}
close(F);
return 1;
}</pre>
Tags: perl, tech
Subscribe
  • Post a new comment

    Error

    default userpic

    Your reply will be screened

    Your IP address will be recorded 

    When you submit the form an invisible reCAPTCHA check will be performed.
    You must follow the Privacy Policy and Google Terms of use.
  • 2 comments