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/

Reply via email to