Setzen Sie die PHP-Ausführung nach dem Senden der HTTP-Antwort fort

Wie kann ich PHP 5.2 (läuft als Apache mod_php) eine komplette HTTP-Antwort an den Client senden und dann die Operationen noch eine Minute lang ausführen?

Die lange Geschichte:

Ich habe ein PHP-Skript, das einige lange databaseanfragen ausführen und E-Mails senden muss, deren Ausführung 45 bis 60 Sekunden in Anspruch nimmt. Dieses Skript wird von einer Anwendung aufgerufen, über die ich keine Kontrolle habe. Ich brauche die Anwendung, um Fehlermeldungen vom PHP-Skript zu melden (meistens ungültige Parametererrors).

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 benötige ich PHP, um die vollständige HTTP-Antwort so schnell wie möglich an den Client zu senden (idealerweise, sobald die Eingabeparameter validiert wurden) und dann die database- und E-Mail-Verarbeitung auszuführen.

Ich pcntl_fork mod_php, daher ist pcntl_fork nicht verfügbar. Ich könnte mich damit herumschlagen, indem ich die zu verarbeitenden Daten in der database speichern und den eigentlichen process von cron aus cron , aber ich suche nach einer kürzeren Lösung.

    Lassen Sie das Skript, das die anfängliche Anforderung verarbeitet, einen Eintrag in einer Verarbeitungswarteschlange erstellen und dann sofort zurückkehren. Erstellen Sie dann einen separaten process (über cron vielleicht), der regelmäßig alle Jobs ausführt, die in der Warteschlange anstehen.

    Ich hatte dieses Snippet in meiner Toolbox “special scripts”, aber es ging verloren (Wolken waren damals nicht üblich), also suchte ich danach und kam zu dieser Frage, überrascht, dass es fehlte, ich suchte mehr und kam zurück hier, 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 benutze es tatsächlich an wenigen Stellen. Und es macht da total Sinn: Ein Banklink gibt die Anfrage einer erfolgreichen Zahlung zurück und ich muss viele Dienste aufrufen und viele Daten verarbeiten, wenn das passiert. Das dauert manchmal mehr als 10 Sekunden, aber der Banklink hat eine feste Timeout-Zeit. Also erkenne ich den Banklink an und zeige ihm den Ausweg, und mache meine Sachen, wenn er schon weg ist.

    Was Sie brauchen, ist diese Art von Setup

    Alt-Text

    Man kann “http fork” zu sich selbst oder einem anderen Skript verwenden. Ich meine etwas in der Art:

     // 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& 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 Kindskript:

     // 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" :) ... 

    Die Hauptidee ist:

    • Parent-Skript, das von einer Benutzeranforderung aufgerufen wird;
    • Parent ruft Child-Skript (dasselbe wie Parent oder anderes) auf demselben Server (oder jedem anderen Server) auf und gibt Anforderungsdaten an sie weiter;
    • Elternteil sagt dem Benutzer zu und endet;
    • Kind arbeitet.

    Wenn Sie mit Kind interagieren müssen – Sie können DB als “Kommunikationsmedium” verwenden: Eltern kann Kindstatus lesen und Befehle schreiben, Kind kann Befehle lesen und Status schreiben. Wenn Sie dies für mehrere untergeordnete Skripts benötigen, sollten Sie die untergeordnete ID auf der Benutzerseite behalten, um sie zu unterscheiden, und diese ID jedes Mal an das übergeordnete Element senden, wenn Sie den Status des jeweiligen untergeordneten Objekts überprüfen möchten.

    Ich habe das hier gefunden – http://linuxportal.ru/forums/index.php/t/22951/

    Was ist mit dem Aufruf eines Skripts auf dem Dateiserver, um so ausgeführt zu werden, als wäre es an der Befehlszeile ausgetriggers worden? Sie können dies mit PHP Exec tun.

    Sie können die PHP-function register-shutdown-function verwenden , die etwas ausführt, nachdem das Skript seinen Dialog mit dem Browser beendet hat.

    Siehe auch ignore_user_abort – aber Sie sollten diese function nicht benötigen, wenn Sie die function register_shutdown_function verwenden. Auf derselben Seite verhindert set_time_limit(0) , dass das Skript eine set_time_limit(0) .

    Mit einer Warteschlange, Exec oder Cron wäre ein Overkill für diese einfache Aufgabe. Es gibt keinen Grund, nicht im selben Skript zu bleiben. Diese Kombination hat mir sehr gut getan:

      ignore_user_abort(true); $response = "some response"; header("Connection: close"); header("Content-Length: " . mb_strlen($response)); echo $response; flush(); // releasing the browser from waiting // continue the script with the slow processing here... 

    Lesen Sie mehr in: Wie kann ich weiterarbeiten, nachdem ich auf eine Ajax-Anfrage in PHP geantwortet habe?

    Sie können eine HTTP-Anfrage zwischen Server und Server erstellen. (kein Browser wird benötigt). Das Geheimnis zum Erstellen einer HTTP-Hintergrundanforderung ist das Festlegen eines sehr kleinen Zeitlimits, sodass die Antwort ignoriert wird.

    Dies ist eine funktionierende function, die ich für diesen Zweck verwendet habe:

    31. Mai PHP asynchrone Hintergrundanfrage Eine weitere Möglichkeit, eine asynchrone Anfrage in PHP zu erstellen (Hintergrundmodus simulieren).

      /** * Another way to make asyncronous (o como se escriba asincrono!) request with php * Con esto se puede simpular un fork en PHP.. nada que envidarle a javita ni C++ * Esta vez usando fsockopen * @author PHPepe * @param unknown_type $url * @param unknown_type $params */ function phpepe_async($url, $params = array()) { $post_params = array(); foreach ($params as $key => &$val) { if (is_array($val)) $val = implode(',', $val); $post_params[] = $key.'='.urlencode($val); } $post_string = implode('&', $post_params); $parts=parse_url($url); $fp = fsockopen($parts['host'], isset($parts['port'])?$parts['port']:80, $errno, $errstr, 30); $out = "POST ".$parts['path']." HTTP/1.1\r\n"; $out.= "Host: ".$parts['host']."\r\n"; $out.= "Content-Type: application/x-www-form-urlencoded\r\n"; $out.= "Content-Length: ".strlen($post_string)."\r\n"; $out.= "Connection: Close\r\n\r\n"; if (isset($post_string)) $out.= $post_string; fwrite($fp, $out); fclose($fp); } // Usage: phpepe_async("http://192.168.1.110/pepe/feng_scripts/phprequest/fork2.php"); 

    Für weitere Informationen können Sie einen Blick auf http://www.phpepe.com/2011/05/php-asynchronous-background-request.html casting

    Es ist möglich, cURL dafür mit einem sehr kurzen Timeout zu verwenden. Dies wäre Ihre Hauptdatei:

     < ?php> $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, "http://example.com/processor.php"); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); curl_setopt($ch, CURLOPT_TIMEOUT_MS, 10); //just some very short timeout curl_exec($ch); curl_close($ch); ?> 

    Und das ist Ihre processor-Datei:

     < ?php ignore_user_abort(true); //very important! for($x = 0; $x < 10; $x++) //do some very time-consuming task sleep(10); ?> 

    Wie Sie sehen, wird das obere Skript nach einer kurzen Zeit (in diesem Fall 10 Millisekunden) ablaufen. Es ist möglich, dass CURLOPT_TIMEOUT_MS nicht so funktioniert, in diesem Fall wäre es äquivalent zu curl_setopt($ch, CURLOPT_TIMEOUT, 1) .

    Wenn also auf die processordatei zugegriffen wurde, wird sie ihre Aufgaben ausführen, unabhängig davon, ob der Benutzer (dh die aufrufende Datei) die Verbindung abbricht.

    Natürlich können Sie auch GET- oder POST-Parameter zwischen den Seiten übergeben.

    Sie können diese functionen in drei Skripte aufteilen. 1. process einleiten und zweiten über exec oder command aufrufen, dies ist auch möglich, über http-Aufruf zu laufen. 2. Die zweite wird die databaseverarbeitung ausführen und am Ende wird die letzte 3 beginnen. Die letzte wird eine E-Mail senden

    Bah, ich habe deine Anforderungen missverstanden. Sieht so aus als wären sie tatsächlich:

    • Script empfängt Eingaben von einer externen Quelle, die Sie nicht steuern
    • Script verarbeitet und validiert die Eingabe und lässt die externe App wissen, ob sie gut ist oder nicht und beendet die Sitzung.
    • Skript startet einen lang andauernden process.

    In diesem Fall, dann ja, mit einer externen Job-Warteschlange und / oder Cron würde funktionieren. Nachdem die Eingabe validiert wurde, fügen Sie die Jobdetails in die Warteschlange ein und schließen Sie sie. Ein anderes Skript kann dann ausgeführt werden, die Jobdetails aus der Warteschlange übernehmen und den längeren process starten. Alex Howansky hat die richtige Idee.

    Entschuldige, ich gebe zu, dass ich das erste Mal ein bisschen überflogen habe.

    Ich würde empfehlen, am Ende eine neue asynchrone Anfrage zu erstellen, anstatt den process mit dem Benutzer fortzusetzen.

    Sie können die andere Anfrage mit der Antwort hier erstellen : Asynchrone PHP-Aufrufe?

    php.ini in Ihrer Apache php.ini Konfigurationsdatei sicher, dass die Ausgabepufferung deaktiviert ist:

     output_buffering = off