[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->SUPER::new($sock);
$self->{service} = $service;
$self->{buf} = ""; # what we've read so far, not forming a complete line
bless $self, ref $class || $class;
$self->watch_read(1);
return $self;
}
# ClientManage
sub event_read {
my $self = shift;
my $bref = $self->read(1024);
return $self->close() unless defined $bref;
$self->{buf} .= $$bref;
if ($self->{buf} =~ s/^(.+?)\r?\n//) {
my $line = $1;
Perlbal::run_manage_command($line, sub {
$self->write("$_[0]\r\n");
});
}
}
# ClientManage
sub event_err { my $self = shift; $self->close; }
sub event_hup { my $self = shift; $self->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 (<F>) {
return 0 unless run_manage_command($_, $writer);
}
close(F);
return 1;
}</pre>
<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->SUPER::new($sock);
$self->{service} = $service;
$self->{buf} = ""; # what we've read so far, not forming a complete line
bless $self, ref $class || $class;
$self->watch_read(1);
return $self;
}
# ClientManage
sub event_read {
my $self = shift;
my $bref = $self->read(1024);
return $self->close() unless defined $bref;
$self->{buf} .= $$bref;
if ($self->{buf} =~ s/^(.+?)\r?\n//) {
my $line = $1;
Perlbal::run_manage_command($line, sub {
$self->write("$_[0]\r\n");
});
}
}
# ClientManage
sub event_err { my $self = shift; $self->close; }
sub event_hup { my $self = shift; $self->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 (<F>) {
return 0 unless run_manage_command($_, $writer);
}
close(F);
return 1;
}</pre>