From a4bd0c541580678d108c383ab268f233ce44b25b Mon Sep 17 00:00:00 2001 From: Moritz Bunkus Date: Thu, 17 Dec 2015 11:56:09 +0100 Subject: [PATCH] =?utf8?q?FCGI:=20Prozess=20nach=20Request=20beenden,=20fa?= =?utf8?q?lls=20belegter=20Speicher=20gr=C3=B6=C3=9Fer=20als=20konfigurier?= =?utf8?q?bares=20Limit?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Perl gibt Speicher nicht wieder ans Betriebssystem zurück. Um zu erreichen, dass nach einigen sehr speicherintensiven Aktionen der Server nicht zu swappen anfangen muss, kann der Administrator nun in der Konfigurationsdatei Limits für den Speicherverbrauch definieren. Werden diese Limits erreicht, so beendet sich der aktuelle Prozess. Diese Prüfung wird erst nach vollständiger Abarbeitung eines Requests durchgeführt. Der FCGI-Manager startet dann bei der nächsten Anfrage automatisch einen neuen Prozess. --- config/kivitendo.conf.default | 15 ++++++++++ dispatcher.fpl | 55 ++++++++++++++++++++++++++++++++++- 2 files changed, 69 insertions(+), 1 deletion(-) diff --git a/config/kivitendo.conf.default b/config/kivitendo.conf.default index 4242d65b1..163984915 100644 --- a/config/kivitendo.conf.default +++ b/config/kivitendo.conf.default @@ -65,6 +65,21 @@ bind_password = # and "en" (English, not perfect) are available. language = de +# The memory limits given here determine the maximum process size +# (vsz, the total amount of memory this process uses including memory +# swapped out or shared with other processes) or resident set size +# (rss, the amount of memory not swapped out/shared with other +# processes). If either limit is reached at the end of the request +# then the kivitendo process will exit. +# +# This only makes sense when running under FCGI. The FCGI manager will +# then automatically start a new process. +# +# Numbers can be postfixed with KB, MB, GB. If no number is given or +# the number is 0 then no checking will be performed. +memory_limit_rss = +memory_limit_vsz = + [paths] # path to temporary files (must be writeable by the web server) userspath = users diff --git a/dispatcher.fpl b/dispatcher.fpl index 7d2dfb5be..5e384ce68 100755 --- a/dispatcher.fpl +++ b/dispatcher.fpl @@ -3,8 +3,58 @@ use strict; use FCGI; +use IO::File; use SL::Dispatcher; use SL::FCGIFixes; +use SL::LXDebug; + +sub _parse_number_with_unit { + my ($number) = @_; + + return undef unless defined $number; + return $number unless $number =~ m{^ \s* (\d+) \s* ([kmg])b \s* $}xi; + + my %factors = (K => 1024, M => 1024 * 1024, G => 1024 * 1024 * 1024); + + return $1 * $factors{uc $2}; +} + +sub _memory_usage_is_too_high { + return undef unless $::lx_office_conf{system}; + + my %limits = ( + rss => _parse_number_with_unit($::lx_office_conf{system}->{memory_limit_rss}), + size => _parse_number_with_unit($::lx_office_conf{system}->{memory_limit_vsz}), + ); + + # $::lxdebug->dump(0, "limits", \%limits); + + return undef unless $limits{rss} || $limits{vsz}; + + my %usage; + + my $in = IO::File->new("/proc/$$/status", "r") or return undef; + + while (<$in>) { + chomp; + $usage{lc $1} = _parse_number_with_unit($2) if m{^ vm(rss|size): \s* (\d+ \s* [kmg]b) \s* $}ix; + } + + $in->close; + + # $::lxdebug->dump(0, "usage", \%usage); + + foreach my $type (keys %limits) { + next if !$limits{$type}; + next if $limits{$type} >= ($usage{$type} // 0); + + $::lxdebug->message(LXDebug::WARN(), "Exiting due to memory size limit reached for type '${type}': limit " . $limits{$type} . " bytes, usage " . $usage{$type} . " bytes"); + + return 1; + } + + return 0; +} our $dispatcher = SL::Dispatcher->new('FastCGI'); $dispatcher->pre_startup_setup; @@ -12,6 +62,9 @@ SL::FCGIFixes::apply_fixes(); $dispatcher->pre_startup_checks; my $request = FCGI::Request(); -$dispatcher->handle_request($request) while $request->Accept() >= 0; +while ($request->Accept() >= 0) { + $dispatcher->handle_request($request); + exit if _memory_usage_is_too_high(); +} 1; -- 2.20.1