baeckerman83: PHP:

Ich habe ein PHP Scrip auf einem Testserver laufen. Dieses verarbeitet XML Dateien. Für jede XML Datei wird per Exec ein neues PHP Script im Hintegrund gestartet.
Jetzt bekomme ich die Meldung das der Arbeitsspeicher voll ist. Ich hab dann mit sar geschaut, dort sieht aber alles gut aus. Wo kann ich noch schauen?
Ich lasse mir im PHP Script schon mit ps aux ermitteln wieviele Scripte ich schon gestartet habe und warte zur Zeit nach 20 Scripten mit Sleep(10) bis es wieder weniger als 20 Scripte sind.

Meldung von etwa 16:28 Uhr :
grep: write error
PHP Warning:  PHP Startup: Unable to load dynamic library '/usr/lib/php5/20100525/curl.so' - /usr/lib/php5/20100525/curl.so: cannot apply additional memory protection after relocation: Cannot allocate memory in Unknown on line 0
/proc/uptime: Nicht genügend Hauptspeicher verfügbar
sh: 0: Cannot fork
sh: 0: Cannot fork

16:00:13    kbmemfree kbmemused  %memused kbbuffers  kbcached  kbcommit   %commit  kbactive   kbinact
16:05:02       910160    662704     42,13         0     65212         0      0,00    447316    195916
16:15:03       969328    603536     38,37         0     76432         0      0,00    301924    281280
16:35:02       938760    634104     40,32         0     72272         0      0,00    375072    241796
16:45:01      1327796    245068     15,58         0     76580         0      0,00    176376     58276
16:55:01      1414840    158024     10,05         0     78524         0      0,00     87020     61588
17:05:01      1414832    158032     10,05         0     78528         0      0,00     85760     62868
17:15:01      1411552    161312     10,26         0     81252         0      0,00     82992     68036

  
[...]  
    if(run_check("db_update_spiel_thread.php")>$max_anzahl_threads){  
        sleep(10);  
        $b--;  
    }else{  
        //echo "ID:".$id_liga."\n";  
        shell_exec($dir."/thread/db_update_spiel_thread.php ".$uebergabe." >> ".$dir."/log/db_update_spiel_goalserve_thread.log 2>&1 &");  
    }  
[...]  
function run_check($runningScript){  
    if( !empty($runningScript) ) {  
  
       // Pruefe wie oft das Hauptscript schon laeuft  
       // Alle weiteren Ausgaben werden dabei ignoriert  
      $output = shell_exec("ps aux | grep {$runningScript} | wc -l");  
  
       // Ergebnis groesser 1: Script laeuft bereits  
       // 1 wird immer geliefert, da das Script sich selbst auch sieht, daher minus 1.  
       return (int)$output-1;  
    }  
}  

  1. Moin!

    Ich lasse mir im PHP Script schon mit ps aux ermitteln wieviele Scripte ich schon gestartet habe und warte zur Zeit nach 20 Scripten mit Sleep(10) bis es wieder weniger als 20 Scripte sind.

    Wieviele Skriptaufrufe machst du aufgrund der XML-Datei wirklich?

    Wie lange läuft eins dieser Unterskripte? (Kommandozeile: "time BEFEHL" sagts dir).

    Wieviel RAM braucht es maximal? (http://php.net/manual/en/function.memory-get-peak-usage.php sagt's dir).

    Willkürlich nach 20 Starts eine Pause von 10 Sekunden zu machen funktioniert ja nur, wenn nach diesen 10 Sekunden diese Skripte beendet sind.

    Wenn die Laufzeit eines einzelnen Skripts hingegen 20 Sekunden beträgt, dann startest du parallel 60 Skripte. Nämlich die ersten 20, dann nach zehn Sekunden nochmal 20, und nach 20 Sekunden fangen die nächsten 20 an, während die allerersten 20 so gerade eben fertig werden.

    Wenn jedes Skript 1 MB an RAM benötigt, würdest du also 60 MB gleichzeitig bereitstellen müssen. Mit 1 MB bist du aber extrem sparsam, vermuten wir doch einfach mal 8 oder 16 MB pro Skript - dann muss dein Server 480 MB oder ein knappes Gigabyte bereitstellen können - und zwar OHNE Swapping! Wenn der Server mit Swapping anfangen würde, hast du sofort verloren, weil sich die Laufzeit jedes Skripts, welches auf Swap-Memory warten muss, exponentiell verlängert. Damit schaukelt sich das System sofort auf, denn nach den 10 Sekunden Wartezeit enden die vorherigen Skripte nicht und geben den Speicher frei, es werden aber neue gestartet, die auch wieder Speicher brauchen.

    Es hängt etwas davon ab, was genau du da eigentlich tun willst, aber vermutlich willst du eine Queue implementieren, und begrenzt parallel abarbeiten. Mit PHP pur kann man da nichts machen (mir ist zumindest nichts bekannt), aber ich würde zu Gearman raten. Gearman ist der Verwalter von abzuarbeitenden Aufgaben und sich dafür bereit meldenden Workern. PHP hat dafür auch Librarys, den Gearman-Server selbst musst du so installieren.

    PHP-Skripte können sich dann als dauerhaft wartende Worker anmelden und auf Aufgaben warten - je nach Anzahl der Worker, die du startest, wird die Aufgabe entsprechend parallel abgearbeitet, es werden aber niemals mehr Worker, als du startest.

    Und dein Hauptskript stellt einfach ohne Pause für jede Aufgabe einen Job bei Gearman ein, ggf. auch direkt mit den zugehörigen Daten, wenn das nicht zuviele sind. Es kann dann auf die Erledigung aller Jobs warten und dabei den Zwischenstand (eingestellte Jobs, erledigte Jobs, noch wartende Jobs) bzw. bei Bedarf und entsprechend programmiertem Worker auch dessen konkreten Fortschritt abfragen und anzeigen - oder du verzichtest darauf und startest einfach nur die Jobs und bist im Hauptskript direkt fertig.

    Du kannst natürlich auch jede andere Queue nehmen, oder was selbst mit MySQL basteln und einen Cronjob drauf ansetzen. Eventuell funktioniert es sogar eine Zeit lang, wenn du durch manuelles Messen, ausrechnen und Sicherheitsmarge hinzufügen die Ausführungshäufigkeit deines jetzigen Systems reduzierst. Aber du bist bei jedem Skript direkt abhängig von der Individualität der jeweiligen Aufgabe, sprich: Wenn EIN Aufruf eines Skripts unerwartet viele Daten zur Bearbeitung in den Speicher tut, und deshalb Swapping einsetzt, reicht das schon aus, um das Gesamtsystem in den Abgrund zu reißen.

    - Sven Rautenberg

    1. Hi Sven,

      vielen dank für deine ausführliche Antwort. Ich warte 10 sekunden und zähle dann ja erst neu ob ich wieder ein Script starten kann. Ich hatte das ganze mit PHP Threads schon versucht, dass war leider auch nicht besser.
      Da schaue ich mir Gearman mal an, das scheint laut deinen Beschreibungen ja das zu können was ich brauche.
      Ich hatte auch schon überlegt, dass PHP vielleicht nicht die beste Sprache für den Anwendungsfall ist und ich eher zu Java oder Python wechseln sollte.