72 Stimmen

PHP-Ausführung nach dem Senden der HTTP-Antwort fortsetzen

Wie kann ich PHP 5.2 (das als apache mod_php läuft) dazu bringen, eine vollständige HTTP-Antwort an den Client zu senden und dann noch eine Minute lang weitere Operationen auszuführen?

Die lange Geschichte:

Ich habe ein PHP-Skript, das einige lange Datenbankabfragen ausführen und E-Mails versenden muss, was 45 bis 60 Sekunden dauert. Dieses Skript wird von einer Anwendung aufgerufen, über die ich keine Kontrolle habe. Ich möchte, dass die Anwendung alle vom PHP-Skript empfangenen Fehlermeldungen meldet (meist Fehler bei ungültigen Parametern).

Die Anwendung hat eine Timeout-Verzögerung von weniger als 45 Sekunden (ich kenne den genauen Wert nicht) und registriert daher jede Ausführung des PHP-Skripts als Fehler. Daher muss PHP die vollständige HTTP-Antwort so schnell wie möglich an den Client senden (idealerweise, sobald die Eingabeparameter validiert wurden) und dann die Datenbank- und E-Mail-Verarbeitung ausführen.

Ich verwende mod_php, also pcntl_fork ist nicht verfügbar. Ich könnte dieses Problem umgehen, indem ich die zu verarbeitenden Daten in der Datenbank speichere und den eigentlichen Prozess von cron aber ich suche nach einer kürzeren Lösung.

5 Stimmen

Entschuldigung, aber das sieht nach einem totalen Missbrauch der PHP-Sprache aus.

3 Stimmen

Es handelt sich weniger um einen Missbrauch der PHP-Sprache als um einen Missbrauch eines Webserver-Prozesses. Wenn kein HTTP/Web mehr involviert ist, sollte kein Webserver damit beschäftigt sein.

83 Stimmen

Systemmissbrauch hin oder her, manchmal müssen wir Dinge tun, die wir nicht mögen, weil sie außerhalb unserer Kontrolle liegen. Das macht die Frage nicht ungültig, es macht die Situation nur unglücklich.

53voto

povilasp Punkte 2376

Ich hatte dieses Snippet in meiner "Spezial-Skripte"-Toolbox, aber es ging verloren (Wolken waren damals nicht üblich), also suchte ich danach und kam auf diese Frage, überrascht zu sehen, dass es fehlt, suchte ich mehr und kam hierher zurück, um es zu posten:

<?php
 ob_end_clean();
 header("Connection: close");
 ignore_user_abort(); // optional
 ob_start();
 echo ('Text the user will see');
 $size = ob_get_length();
 header("Content-Length: $size");
 ob_end_flush(); // Strange behaviour, will not work
 flush();            // Unless both are called !
 session_write_close(); // Added a line suggested in the comment
 // Do processing here 
 sleep(30);
 echo('Text user will never see');
?>

Ich verwende sie eigentlich nur an wenigen Stellen. Und dort macht es durchaus Sinn: ein Banklink gibt die Anfrage einer erfolgreichen Zahlung zurück und ich muss eine Menge Dienste aufrufen und eine Menge Daten verarbeiten, wenn das passiert. Das dauert manchmal mehr als 10 Sekunden, aber der Banklink hat eine feste Timeout-Zeit. Also bestätige ich den Banklink und zeige ihm den Weg nach draußen, und erledige meine Sachen, wenn er schon weg ist.

29voto

Alex Howansky Punkte 47344

Lassen Sie das Skript, das die erste Anfrage bearbeitet, einen Eintrag in einer Verarbeitungswarteschlange erstellen und dann sofort zurückkehren. Erstellen Sie dann einen separaten Prozess (z. B. über Cron), der regelmäßig alle in der Warteschlange anstehenden Aufträge ausführt.

8voto

SomeGuy Punkte 81

Man kann "http fork" zu sich selbst oder einem anderen Skript verwenden. Ich meine so etwas wie dieses:

// parent sript, called by user request from browser

// create socket for calling child script
$socketToChild = fsockopen("localhost", 80);

// HTTP-packet building; header first
$msgToChild = "POST /sript.php?&param=value&<more params> HTTP/1.0\n";
$msgToChild .= "Host: localhost\n";
$postData = "Any data for child as POST-query";
$msgToChild .= "Content-Length: ".strlen($postData)."\n\n";

// header done, glue with data
$msgToChild .= $postData;

// send packet no oneself www-server - new process will be created to handle our query
fwrite($socketToChild, $msgToChild);

// wait and read answer from child
$data = fread($socketToChild, $dataSize);

// close connection to child
fclose($socketToChild);
...

Jetzt das Kinderskript:

// parse HTTP-query somewhere and somehow before this point

// "disable partial output" or 
// "enable buffering" to give out all at once later
ob_start();

// "say hello" to client (parent script in this case) disconnection
// before child ends - we need not care about it
ignore_user_abort(1);

// we will work forever
set_time_limit(0);

// we need to say something to parent to stop its waiting
// it could be something useful like client ID or just "OK"
...
echo $reply;

// push buffer to parent
ob_flush();

// parent gets our answer and disconnects
// but we can work "in background" :)
...

Der Grundgedanke ist:

  • übergeordnetes Skript, das durch eine Benutzeranfrage aufgerufen wird;
  • Das übergeordnete Skript ruft das untergeordnete Skript (das gleiche wie das übergeordnete oder ein anderes) auf demselben Server (oder einem anderen Server) auf und übergibt diesem die Anforderungsdaten;
  • Das Elternteil sagt dem Benutzer "OK" und endet;
  • Kind arbeitet.

Wenn Sie mit dem Kind interagieren müssen, können Sie DB als "Kommunikationsmedium" verwenden: Eltern können den Status des Kindes lesen und Befehle schreiben, das Kind kann Befehle lesen und den Status schreiben. Wenn Sie dies für mehrere Child-Skripte benötigen, sollten Sie die Child-ID auf der Benutzerseite behalten, um sie zu unterscheiden, und diese ID jedes Mal an das Parent senden, wenn Sie den Status des jeweiligen Childs überprüfen wollen.

Das habe ich hier gefunden - http://linuxportal.ru/forums/index.php/t/22951/

8voto

Yanick Rochon Punkte 47683

Was Sie brauchen, ist diese Art von Einrichtung

alt text

5voto

SorcyCat Punkte 1208

Wie wäre es, ein Skript auf dem Dateiserver aufzurufen, um es so auszuführen, als ob es über die Befehlszeile ausgelöst worden wäre? Sie können dies mit PHPs Ausführung .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X