On Wed, Jul 14, 2004 at 01:46:11PM +0200, Hildo Biersma wrote:
> I am running a POE server that handles multiple clients, each with a
> long-lived TCP/IP connection. A client will send at least one request
> and get a reply, and can optionally send additional requests. The
> client can reconenct if the server hangs up the conenction between
> requests. The server keeps a client connection alive for two minutes
> after the last request has been handled, and then shuts it down.
>
> At the beginning of handling a client request:
>
> $kernel->alarm_remove($heap->{AlarmId}) if (defined $heap->{AlarmId});
>
> At the end of a client request, after put(response):
>
> $heap->{AlarmId} = $kernel->delay_set('timeout', 120);
>
> In the alarm event handler:
>
> $_[KERNEL]->yield("shutdown")
>
> This is working fine. However, in some cases, my server gets a flood of
> client requests, where it would survive fine without the time-out,
> but runs out of file descriptors with the keep-alive timeout.
>
> What I would like to do is catch the condition that I am getting close
> to running out of file descriptors, and then close all those client fds
> that are idle. I realize I'll have to keep a data structure of idle
> client connections; what I don't know is how to properly hook into POE
> to count the number of current open fds (across all ports that POE
> listens to). Where and how can I cleanly do this? I can hook into the
> 'Acceptor' of the POE::Component::Server::TCP class, but don't see a
> corresponding callback to track all connection close events. ANd it
> would be nice not to have to count fds myself :-)
Don't use Acceptor. Use ClientConnected and ClientDisconnected.
my $current_connections = 0;
my %last_activity_time;
POE::Component::Server::TCP->new(
... everything else ...,
ClientConnected => sub {
$current_connections++;
$last_activity_time{$_[SESSION]->ID} = time;
},
ClientDisconnected => sub {
$current_connections--;
delete $last_activity_time{$_[SESSION]->ID};
},
ClientInput => sub {
... all your other work ...;
$last_activity_time{$_[SESSION]->ID} = time;
},
);
When you need to sweep out old connections:
# If more than 90% of the available file descriptors are used...
# Shutdown half of the active connections. Choose the ones that
# have been alive the longest (least recently used).
if ($current_connections > MAX_CONNECTIONS * 0.9) {
my $half_of_them = $current_connections / 2;
foreach my $session_id (
sort { $last_activity_time{$b} <=> $last_activity_time{$a} }
keys %last_activity_time
) {
last if $half_of_them-- < 1;
$kernel->post($session_id, "shutdown");
}
}
All this code's untested. Good luck.
--
Rocco Caputo - http://poe.perl.org/