Cliff,

Here's the diff of src/main/http_main.c No children are spawned if the
file /tmp/spawn.lock is readable by the root webserver.
I also post yet another watchdog script. It touches the spawnlock file and
kills/shuts down too large apache children. 
It uses the /proc filesystem heavily, so might not be too portable.

Enjoy,

-Balazs

[balazs@felix apache_1.3.14]$ diff src/main/http_main.c.ORIG src/main/http_main.c
4419a4420,4423
> /* BALAZSD */
>     int spawn_lock=0;
>     FILE *SPAWN;
> /* BALAZSD */
4483a4488,4495
> 
> /* BALAZSD */
>     if( SPAWN=fopen( "/tmp/spawn.lock", "r" )){
>       fclose( SPAWN );
>       spawn_lock = 1;
>     }
> /* BALAZSD */
>     
4506a4519,4526
> /* BALAZSD */
>       else if ( spawn_lock ) {
>         ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, server_conf,
>                        "Server would've spawned, but lockfile found");
> 
>         idle_spawn_rate = 1;
>       }
> /* BALAZSD */



#!/usr/bin/perl
use strict;

# ===================== D E C L A R A T I O N S ===================

#These are the variables that are worth changing through USR2
use vars qw(  $STOPPED $INTERVAL $DEFAULT $USR1LIM $TERMLIM $SPAWNLIM );

# Bad globals...
use vars qw( $opt_i $opt_s $FREEMEM $LOADAVG $CPUUTIL $CHILDINFO $SPAWNLOCK_SET 
@SIGKILLNEXT);

# ======================== C O N F I G =============================

# File to log to using append
my $LOGFILE    = "/tmp/balazsd.log";

# File that stops apache from spawning news kids
my $SPAWNLOCK = "/tmp/spawn.lock";

require 'getopts.pl';
&Getopts('i:s:');

# Default interval at which processes are checked
$INTERVAL = $opt_i || 3;

# =================== E N D   O F   C O N F I G =====================
# Detach
exit if fork();
`renice -20 $$`;
# Or have some other way to run this as max priority
# For example start as root and then change user to web

$| = 1;

my $scriptname = $opt_s || usage();
logme ("Monitoring <$scriptname> at $INTERVAL seconds with PID #$$\n");

$SIG{TERM} = sub { $STOPPED = 1 };
$SIG{USR1} = sub { $DEFAULT = 1 };
my $default_action = \&autopilot_action;
$SIG{USR2} = \&handle_usr2;

# Default values for the limits
$USR1LIM  = 45_000_000; # send SIGUSR1 to a process if uses more mem then
$USR1LIM
$TERMLIM  = 80_000_000; #  ||  SIGTERM               ||
$SPAWNLIM = 30_000_000; # Stop apache spawning new kids if free mem <
$SPAWNLIM

my ($oldstarttime, $oldstartproc, $starttime, $startproc);

while( not $STOPPED ){
  ( $oldstarttime, $oldstartproc ) = ($starttime, $startproc);
  ($starttime, $startproc) = getproctime();
  $CPUUTIL = 100 * (1-( $startproc-$oldstartproc ) / (
$starttime-$oldstarttime ));

  gather_data();

  if( $DEFAULT ){
    logme("!!! AUTOPILOT MODE !!!");
    &$default_action();
  }
  else {
    eval {action()};
    logme("action ERROR: $@") if $@;
  }

  my $currtime;
  ($currtime, undef) = getproctime();
  my $sleeptime = $starttime + $INTERVAL - $currtime;
  select undef, undef, undef, $sleeptime  if $sleeptime > 0;
}

logme ("Finished successfully\n");
exit 0;

# =========================== S U B S ===============================

sub usage {
  warn "Usage: $0 -s scriptname [ -i interval ]\n";
  exit 1;
}

sub logme {
  my $extralinefeed = $_[$#_] =~ /\n$/ ? "" : "\n";
  open LOGFILE, ">>$LOGFILE";
  print LOGFILE "balazsd: ", scalar localtime, ": @_$extralinefeed";
  close LOGFILE;
}

sub getproctime {
  unless ( open (UPTIME, "/proc/uptime")){
    logme ("getproctime failed: $!\n");
    return ( 0 , 0 );
  }
  my ($rtime, $utime) = split " ", <UPTIME>;
  close UPTIME;
  ($rtime, $utime);
}

sub handle_usr2 {
  do "/tmp/balazsd.eval";
# FIXME
  `(echo; echo;echo -n "At ";date;cat /tmp/balazsd.eval;echo =================) >> 
/tmp/balazsd.eval.log`;
  if( $@ ){
    logme("handle_usr2 ERROR $@");
  }
  else {
    logme("Caught USR2 and evaled code successfully.");
  }
}

# This is the piece of code that is always executed when
# $DEFAULT == 1 no matter what was evaled.
sub autopilot_action {
  my $lockfile = $FREEMEM < $SPAWNLIM ? 1 : 0;
  if( $lockfile ){
    open SPAWNLOCK , ">$SPAWNLOCK";
    print SPAWNLOCK ".";
    close SPANLOCK;
    logme("Setting SPANLOCK") if ! $SPAWNLOCK_SET;
    $SPAWNLOCK_SET = 1;
  }
  else {
    unlink $SPAWNLOCK;
    logme("Releasing SPANLOCK") if $SPAWNLOCK_SET;
    $SPAWNLOCK_SET = 0;
  }

  for ( @SIGKILLNEXT ){
    if( kill 0, $_){
      kill 9, $_ ;
      logme (" $_ SIGKILLED");
    }
  }
  @SIGKILLNEXT = ();

  for my $pid ( keys %{$CHILDINFO->{children}} ){
    if( $CHILDINFO->{children}{$pid}{mem} > $TERMLIM ){
      kill 15, $pid;
      push @SIGKILLNEXT, $pid;
      logme(" $pid SIGTERM-ed");
    }
    elsif ( $CHILDINFO->{children}{$pid}{mem} > $USR1LIM ){
      kill 10, $pid;
      logme(" $pid SIGUSR1-ed");
    }
  }
}

# The default for action is of course autopilot_action
sub action {
  autopilot_action();
}

sub gather_data {
  &read_loadavg();
  &read_freemem();
  &read_childinfo();
  logme("MEM: $FREEMEM LOADAVG: $LOADAVG CPUUTIL: $CPUUTIL");
}

sub read_loadavg {
  open LOADAVG, "/proc/loadavg";
  ($LOADAVG, undef) = split / /, <LOADAVG>, 2;
  close (LOADAVG );
}

sub read_freemem {
  open FREEMEM, "/proc/meminfo";
  <FREEMEM>;
  (undef, undef, undef, $FREEMEM, undef ) = split / +/, <FREEMEM>;
  close FREEMEM;
}

sub read_childinfo {
  delete $CHILDINFO->{children};
  chdir "/proc";
  my @pids = <*>;
  for my $pid ( @pids ){
    next unless $pid =~ /^\d+$/;
    my (@pidstat);
    open PIDSTAT, "$pid/stat";
    @pidstat = split / +/, <PIDSTAT>;
    close PIDSTAT;
    next if $pidstat[3] == 1; # Don't take the parent
    next unless $pidstat[1] =~ /^\($opt_s\)/;
    $CHILDINFO->{children}{$pid}{mem} = $pidstat[22];
  }
}


On Wed, 17 Jan 2001, ___cliff rayman___ wrote:

> i think its worth posting to the list.  it will be forever in the
> archives when someone needs it.
> 
> thanks!
> 
> Balazs Rauznitz wrote:
> 
> > On Wed, 17 Jan 2001, ___cliff rayman___ wrote:
> >
> > > i and others have written on the list before, that pushing apache
> > > children into swap causes a rapid downward spiral in performance.
> > > I don't think that MaxClients is the right way to limit the # of children.  i 
>think
> > > MaxSpareCoreMemory would make more sense.  You could
> > > set this to 1K if your server was designated for Apache
> > > only, or set it to a higher value if it were a multipurpose machine.
> > > mod_perl/apache and paging/swapping just don't mix.
> >
> > Once I wrote a patch to apache so that it would not spawn new children if
> > a certain file was present in the filesystem. You can then have a watchdog
> > process touch or delete that file based on any criteria you want. Imo
> > having a separate and flexible process is better than apache trying to
> > make these decisions...
> > I'll dig it up if interested.
> >
> > -Balazs
> 
> --
> ___cliff [EMAIL PROTECTED]http://www.genwax.com/
> 
> 

Reply via email to