/usr/share/perl5/Perlbal/ReproxyManager.pm is in libperlbal-perl 1.80-3.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 | # HTTP connection to non-pool backend nodes (probably fast event-based webservers)
#
# Copyright 2004, Danga Interactive, Inc.
# Copyright 2005-2007, Six Apart, Ltd.
#
package Perlbal::ReproxyManager;
use strict;
use warnings;
no warnings qw(deprecated);
# class storage to store 'host:ip' => $service objects, for making
# reproxies use a service that you can then track
our $ReproxySelf;
our %ReproxyConnecting; # ( host:ip => $backend ); keeps track of outstanding connections to backend that
# are in the connecting state
our %ReproxyBored; # ( host:ip => [ $backend, ... ] ); list of our bored backends
our %ReproxyQueues; # ( host:ip => [ $clientproxy, ... ] ); queued up requests for this backend
our %ReproxyBackends; # ( host:ip => [ $backend, ... ] ); array of backends we have connected
our %ReproxyMax; # ( host:ip => int ); maximum number of connections to have open at any one time
our $ReproxyGlobalMax; # int; the global cap used if no per-host cap is specified
our $NoSpawn = 0; # bool; when set, spawn_backend immediately returns without running
our $LastCleanup = 0; # int; time we last ran our cleanup logic (FIXME: temp hack)
Perlbal::track_var("rep_connecting", \%ReproxyConnecting);
Perlbal::track_var("rep_bored", \%ReproxyBored);
Perlbal::track_var("rep_queues", \%ReproxyQueues);
Perlbal::track_var("rep_backends", \%ReproxyBackends);
# singleton new function; returns us if we exist, else creates us
sub get {
return $ReproxySelf if $ReproxySelf;
# doesn't exist, so create it and return it
my $class = shift;
my $self = {};
bless $self, $class;
return $ReproxySelf = $self;
}
# given (clientproxy, primary_res_hdrs), initiate proceedings to process a
# request for a reproxy resource
sub do_reproxy {
my Perlbal::ReproxyManager $self = Perlbal::ReproxyManager->get; # singleton
my Perlbal::ClientProxy $cp = $_[0];
return undef unless $self && $cp;
# get data we use
my $datref = $cp->{reproxy_uris}->[0];
my $ipport = "$datref->[0]:$datref->[1]";
push @{$ReproxyQueues{$ipport} ||= []}, $cp;
# see if we should do cleanup (FIXME: temp hack)
my $now = time();
if ($LastCleanup < $now - 5) {
# remove closed backends from our array. this is O(n) but n is small
# and we're paranoid that just keeping a count would get corrupt over
# time. also removes the backends that have clients that are closed.
@{$ReproxyBackends{$ipport}} = grep {
! $_->{closed} && (! $_->{client} || ! $_->{client}->{closed})
} @{$ReproxyBackends{$ipport}};
$LastCleanup = $now;
}
# now start a new backend
$self->spawn_backend($ipport);
return 1;
}
# part of the reportto interface; this is called when a backend is unable to establish
# a connection with a backend. we simply try the next uri.
sub note_bad_backend_connect {
my Perlbal::ReproxyManager $self = $_[0];
my Perlbal::BackendHTTP $be = $_[1];
# decrement counts and undef connecting backend
$ReproxyConnecting{$be->{ipport}} = undef;
# if nobody waiting, doesn't matter if we couldn't get to this backend
return unless @{$ReproxyQueues{$be->{ipport}} || []};
# if we still have some connected backends then ignore this bad connection attempt
return if scalar @{$ReproxyBackends{$be->{ipport}} || []};
# at this point, we have no connected backends, and our connecting one failed
# so we want to tell all of the waiting clients to try their next uri, because
# this host is down.
while (my Perlbal::ClientProxy $cp = shift @{$ReproxyQueues{$be->{ipport}}}) {
$cp->try_next_uri;
}
return 1;
}
# called by a backend when it's ready for a request
sub register_boredom {
my Perlbal::ReproxyManager $self = $_[0];
my Perlbal::BackendHTTP $be = $_[1];
# if this backend was connecting
my $ipport = $be->{ipport};
if ($ReproxyConnecting{$ipport} && $ReproxyConnecting{$ipport} == $be) {
$ReproxyConnecting{$ipport} = undef;
$ReproxyBackends{$ipport} ||= [];
push @{$ReproxyBackends{$ipport}}, $be;
}
# sometimes a backend is closed but it tries to register with us anyway... ignore it
# but since this might have been our only one, spawn another
if ($be->{closed}) {
$self->spawn_backend($ipport);
return;
}
# find some clients to use
while (my Perlbal::ClientProxy $cp = shift @{$ReproxyQueues{$ipport} || []}) {
# safety checks
next if $cp->{closed};
# give backend to client
$cp->use_reproxy_backend($be);
return;
}
# no clients if we get here, so push onto bored backend list
push @{$ReproxyBored{$ipport} ||= []}, $be;
# clean up the front of our list if we can (see docs above)
if (my Perlbal::BackendHTTP $bbe = $ReproxyBored{$ipport}->[0]) {
if ($bbe->{alive_time} < time() - 5) {
$NoSpawn = 1;
$bbe->close('have_newer_bored');
shift @{$ReproxyBored{$ipport}};
$NoSpawn = 0;
}
}
return 0;
}
# backend closed, decrease counts, etc
sub note_backend_close {
my Perlbal::ReproxyManager $self = $_[0];
my Perlbal::BackendHTTP $be = $_[1];
# remove closed backends from our array. this is O(n) but n is small
# and we're paranoid that just keeping a count would get corrupt over
# time.
@{$ReproxyBackends{$be->{ipport}}} = grep {
! $_->{closed}
} @{$ReproxyBackends{$be->{ipport}}};
# spawn more if needed
$self->spawn_backend($be->{ipport});
}
sub spawn_backend {
return if $NoSpawn;
my Perlbal::ReproxyManager $self = $_[0];
my $ipport = $_[1];
# if we're already connecting, we don't want to spawn another one
if (my Perlbal::BackendHTTP $be = $ReproxyConnecting{$ipport}) {
# see if this one is too old?
if ($be->{create_time} < (time() - 5)) { # older than 5 seconds?
$self->note_bad_backend_connect($be);
$be->close("connection_timeout");
# we return here instead of spawning because closing the backend calls
# note_backend_close which will call spawn_backend again, and at that
# point we won't have a pending connection and can spawn
return;
} else {
# don't spawn more if we're already connecting
return;
}
}
# if nobody waiting, don't spawn extra connections
return unless @{$ReproxyQueues{$ipport} || []};
# don't spawn if we have a bored one already
while (my Perlbal::BackendHTTP $bbe = pop @{$ReproxyBored{$ipport} || []}) {
# don't use keep-alive connections if we know the server's
# just about to kill the connection for being idle
my $now = time();
if ($bbe->{disconnect_at} && $now + 2 > $bbe->{disconnect_at} ||
$bbe->{alive_time} < $now - 5)
{
$NoSpawn = 1;
$bbe->close("too_close_disconnect");
$NoSpawn = 0;
next;
}
# it's good, give it to someone
$self->register_boredom($bbe);
return;
}
# see if we have too many already?
my $max = $ReproxyMax{$ipport} || $ReproxyGlobalMax || 0;
my $count = scalar @{$ReproxyBackends{$ipport} || []};
return if $max && ($count >= $max);
# start one connecting and enqueue
my $be = Perlbal::BackendHTTP->new(undef, split(/:/, $ipport), { reportto => $self })
or return 0;
$ReproxyConnecting{$ipport} = $be;
}
sub backend_response_received {
my Perlbal::ReproxyManager $self = $_[0];
my Perlbal::BackendHTTP $be = $_[1];
my Perlbal::ClientProxy $cp = $be->{client};
# if no client, close backend and return 1
unless ($cp) {
$be->close("lost_client");
return 1;
}
# pass on to client
return $cp->backend_response_received($be);
}
sub dump_state {
my $out = shift;
return unless $out;
# spits out what we have connecting
while (my ($hostip, $dat) = each %ReproxyConnecting) {
$out->("connecting $hostip 1") if defined $dat;
}
while (my ($hostip, $dat) = each %ReproxyBored) {
$out->("bored $hostip " . scalar(@$dat));
}
while (my ($hostip, $dat) = each %ReproxyQueues) {
$out->("clients_queued $hostip " . scalar(@$dat));
}
while (my ($hostip, $dat) = each %ReproxyBackends) {
$out->("backends $hostip " . scalar(@$dat));
foreach my $be (@$dat) {
$out->("... " . $be->as_string);
}
}
while (my ($hostip, $dat) = each %ReproxyMax) {
$out->("SERVER max_reproxy_connections($hostip) = $dat");
}
$out->("SERVER max_reproxy_connections = " . ($ReproxyGlobalMax || 0));
$out->('.');
}
1;
|