Ich versuche, einen AJAX-Aufruf (über JQuery) auszuführen, der einen ziemlich langen Prozess initiiert. Ich möchte, dass das Skript einfach eine Antwort sendet, die angibt, dass der Prozess gestartet wurde, aber JQuery gibt die Antwort erst zurück, wenn das PHP-Skript ausgeführt wurde.
Ich habe dies mit einem "close" -Header (unten) und auch mit Ausgabepufferung versucht. beides scheint nicht zu funktionieren. Irgendwelche Ideen? oder muss ich das in JQuery tun?
<?php
echo( "We'll email you as soon as this is done." );
header( "Connection: Close" );
// do some stuff that will take a while
mail( '[email protected]', "okay I'm done", 'Yup, all done.' );
?>
Antworten:
Die folgende PHP-Handbuchseite (inkl. Benutzerhinweise) enthält mehrere Anweisungen zum Schließen der TCP-Verbindung zum Browser, ohne das PHP-Skript zu beenden:
Angeblich erfordert es etwas mehr als das Senden eines engen Headers.
OP bestätigt dann: yup, das hat den Trick gemacht: Verweis auf User Note # 71172 (Nov 2006) hier kopiert:
Später im Juli 2010 verknüpfte Arctic Fire in einer verwandten Antwort zwei weitere Benutzerhinweise, die Folgemaßnahmen zu den oben genannten waren:
quelle
Connection: close
Header von der anderen Software im Stack überschrieben werden kann, z. B. der Reverse-Proxy bei einem CGI (ich habe dieses Verhalten bei Nginx beobachtet). Siehe die Antwort von @hanshenrik dazu. Wird im AllgemeinenConnection: close
auf der Clientseite ausgeführt und sollte nicht als Antwort auf diese Frage betrachtet werden. Die Verbindung sollte von der Serverseite aus geschlossen werden .Es ist notwendig, diese 2 Header zu senden:
Da Sie die Größe Ihrer Ausgabe kennen müssen, müssen Sie Ihre Ausgabe puffern und dann in den Browser leeren:
Wenn Ihr Webserver die automatische gzip-Komprimierung für die Ausgabe verwendet (z. B. Apache mit mod_deflate), funktioniert dies nicht, da die tatsächliche Größe der Ausgabe geändert wird und die Inhaltslänge nicht mehr genau ist. Deaktivieren Sie die gzip-Komprimierung des jeweiligen Skripts.
Weitere Informationen finden Sie unter http://www.zulius.com/how-to/close-browser-connection-continue-execution
quelle
header("Content-Encoding: none\r\n");
Weise wird sie von Apache nicht komprimiert.ob_flush()
ist nicht nötig und verursacht tatsächlich einen Hinweisfailed to flush buffer
. Ich habe es herausgenommen und das hat super funktioniert.ob_flush()
Linie war notwendig.Sie können Fast-CGI mit PHP-FPM verwenden, um die
fastcgi_end_request()
Funktion zu verwenden . Auf diese Weise können Sie die Verarbeitung fortsetzen, während die Antwort bereits an den Client gesendet wurde.Sie finden dies im PHP-Handbuch hier: FastCGI Process Manager (FPM) ; Diese Funktion ist jedoch im Handbuch nicht weiter dokumentiert. Hier der Auszug aus dem PHP-FPM: PHP FastCGI Process Manager Wiki :
fastcgi_finish_request ()
Geltungsbereich: PHP-Funktion
Kategorie: Optimierung
Mit dieser Funktion können Sie die Implementierung einiger PHP-Abfragen beschleunigen. Eine Beschleunigung ist möglich, wenn während der Skriptausführung Aktionen ausgeführt werden, die sich nicht auf die Serverantwort auswirken. Das Speichern der Sitzung in Memcached kann beispielsweise erfolgen, nachdem die Seite erstellt und an einen Webserver übergeben wurde.
fastcgi_finish_request()
ist eine PHP-Funktion, die die Antwortausgabe stoppt. Der Webserver beginnt sofort, die Antwort "langsam und traurig" an den Client zu übertragen, und gleichzeitig kann PHP im Kontext einer Abfrage viele nützliche Dinge tun, z. B. das Speichern der Sitzung, das Konvertieren des heruntergeladenen Videos und das Behandeln aller Arten von Statistiken usw.fastcgi_finish_request()
kann die Ausführung der Abschaltfunktion aufrufen.Hinweis:
fastcgi_finish_request()
hat eine Marotte , wo Anrufeflush
,print
oderecho
wird das Skript vorzeitig beenden.Um dieses Problem zu vermeiden, können Sie
ignore_user_abort(true)
direkt vor oder nach demfastcgi_finish_request
Anruf anrufen:quelle
Vollversion:
quelle
Eine bessere Lösung besteht darin, einen Hintergrundprozess zu verzweigen. Unter Unix / Linux ist es ziemlich einfach:
Sie sollten sich diese Frage für bessere Beispiele ansehen:
PHP führt einen Hintergrundprozess aus
quelle
Angenommen, Sie haben einen Linux-Server und Root-Zugriff, versuchen Sie dies. Es ist die einfachste Lösung, die ich gefunden habe.
Erstellen Sie ein neues Verzeichnis für die folgenden Dateien und erteilen Sie ihm die vollständigen Berechtigungen. (Wir können es später sicherer machen.)
Fügen Sie dies in eine Datei mit dem Namen ein
bgping
.Beachten Sie die
&
. Der Ping-Befehl wird im Hintergrund ausgeführt, während der aktuelle Prozess zum Echo-Befehl übergeht. Es wird 15 Mal ein Ping an www.google.com gesendet, was ungefähr 15 Sekunden dauert.Machen Sie es ausführbar.
Fügen Sie dies in eine Datei mit dem Namen ein
bgtest.php
.Wenn Sie bgtest.php in Ihrem Browser anfordern, sollten Sie schnell die folgende Antwort erhalten, ohne etwa 15 Sekunden auf den Abschluss des Ping-Befehls warten zu müssen.
Der Ping-Befehl sollte jetzt auf dem Server ausgeführt werden. Anstelle des Befehls ping können Sie auch ein PHP-Skript ausführen:
Hoffe das hilft!
quelle
Hier ist eine Modifikation von Timbos Code, die mit der gzip-Komprimierung funktioniert.
quelle
Ich bin auf einem gemeinsam genutzten Host und bin so eingerichtet
fastcgi_finish_request
, dass Skripte vollständig beendet werden. Ich mag dieconnection: close
Lösung auch nicht. Durch die Verwendung wird eine separate Verbindung für nachfolgende Anforderungen erzwungen, was zusätzliche Serverressourcen kostet. Ich habe denTransfer-Encoding: cunked
Wikipedia-Artikel gelesen und erfahren, dass0\r\n\r\n
eine Antwort beendet wird. Ich habe dies nicht gründlich auf allen Browserversionen und Geräten getestet, aber es funktioniert auf allen 4 meiner aktuellen Browser.quelle
Sie könnten versuchen, Multithreading durchzuführen.
Sie könnten ein Skript erstellen , das einen Systemaufruf ausführt (mithilfe von shell_exec ), der die PHP-Binärdatei mit dem Skript aufruft, um Ihre Arbeit als Parameter auszuführen . Aber ich denke nicht, dass dies der sicherste Weg ist. Vielleicht können Sie Dinge aufhellen, indem Sie den PHP-Prozess und andere Dinge chrooten
Alternativ gibt es eine Klasse bei phpclasses, die dies tut http://www.phpclasses.org/browse/package/3953.html . Aber ich kenne die Einzelheiten der Implementierung nicht
quelle
&
Zeichen, um den Prozess im Hintergrund auszuführen.TL; DR Antwort:
Funktionsantwort:
quelle
Ihr Problem kann durch parallele Programmierung in PHP gelöst werden. Ich habe vor einigen Wochen hier eine Frage dazu gestellt: Wie kann man Multithreading in PHP-Anwendungen verwenden?
Und bekam tolle Antworten. Besonders gut hat mir einer gefallen. Der Autor hat auf das Tutorial Easy Parallel Processing in PHP (September 2008; von Johnlim) verwiesen, das Ihr Problem tatsächlich sehr gut lösen kann, da ich es bereits verwendet habe, um ein ähnliches Problem zu lösen, das vor einigen Tagen aufgetreten ist.
quelle
Die Antwort von Joeri Sebrechts ist knapp, zerstört jedoch alle vorhandenen Inhalte, die möglicherweise gepuffert werden, bevor Sie die Verbindung trennen möchten. Es wird nicht
ignore_user_abort
richtig aufgerufen , sodass das Skript vorzeitig beendet werden kann. Die Antwort des DIYismus ist gut, aber nicht allgemein anwendbar. Beispielsweise kann eine Person mehr oder weniger Ausgabepuffer haben, die diese Antwort nicht verarbeitet, sodass sie in Ihrer Situation möglicherweise einfach nicht funktioniert und Sie nicht wissen, warum.Mit dieser Funktion können Sie die Verbindung jederzeit trennen (solange noch keine Header gesendet wurden) und den bisher generierten Inhalt beibehalten. Die zusätzliche Bearbeitungszeit ist standardmäßig unbegrenzt.
Wenn Sie auch zusätzlichen Speicher benötigen, weisen Sie ihn zu, bevor Sie diese Funktion aufrufen.
quelle
Hinweis für mod_fcgid-Benutzer (bitte auf eigenes Risiko verwenden).
Schnelle Lösung
Die akzeptierte Antwort von Joeri Sebrechts ist in der Tat funktional. Wenn Sie jedoch mod_fcgid verwenden, stellen Sie möglicherweise fest, dass diese Lösung nicht alleine funktioniert. Mit anderen Worten, wenn die Flush- Funktion aufgerufen wird, wird die Verbindung zum Client nicht geschlossen.
Der
FcgidOutputBufferSize
Konfigurationsparameter von mod_fcgid kann schuld sein. Ich habe diesen Tipp gefunden in:Nachdem Sie das Obige gelesen haben, können Sie zu dem Schluss kommen, dass eine schnelle Lösung darin besteht, die Zeile hinzuzufügen (siehe "Beispiel für einen virtuellen Host" am Ende):
entweder in Ihrer Apache-Konfigurationsdatei (z. B. httpd.conf), Ihrer FCGI-Konfigurationsdatei (z. B. fcgid.conf) oder in Ihrer virtuellen Hosts-Datei (z. B. httpd-vhosts.conf).
Details & eine zweite Lösung
Die obige Lösung deaktiviert die von mod_fcgid durchgeführte Pufferung entweder für den gesamten Server oder für einen bestimmten virtuellen Host. Dies kann zu einer Leistungsminderung für Ihre Website führen. Auf der anderen Seite ist dies möglicherweise nicht der Fall, da PHP die Pufferung selbst durchführt.
Falls Sie die Pufferung von mod_fcgid nicht deaktivieren möchten , gibt es eine andere Lösung ... Sie können diesen Puffer zum Leeren zwingen .
Der folgende Code macht genau das, indem er auf der von Joeri Sebrechts vorgeschlagenen Lösung aufbaut:
Die hinzugefügte Codezeile füllt im Wesentlichen den Puffer von mod_fcgi und zwingt ihn zum Leeren . Die Nummer "65537" wurde gewählt, da der Standardwert der
FcgidOutputBufferSize
Variablen "65536" ist, wie auf der Apache-Webseite für die entsprechende Anweisung angegeben . Daher müssen Sie diesen Wert möglicherweise entsprechend anpassen, wenn in Ihrer Umgebung ein anderer Wert festgelegt ist.Meine Umgebung
Beispiel für einen virtuellen Host
quelle
das hat bei mir funktioniert
quelle
Ok, im Grunde genommen funktioniert jQuery die XHR-Anforderung nicht, selbst die ob_flush-Methode funktioniert nicht, da Sie nicht bei jedem onreadystatechange eine Funktion ausführen können. jQuery überprüft den Status und wählt dann die richtigen Aktionen aus (abgeschlossen, Fehler, Erfolg, Zeitüberschreitung). Und obwohl ich keine Referenz finden konnte, erinnere ich mich, dass dies nicht bei allen XHR-Implementierungen funktioniert. Eine Methode, von der ich glaube, dass sie für Sie funktionieren sollte, ist eine Kreuzung zwischen ob_flush und Forever-Frame-Polling.
Und weil die Skripte inline ausgeführt werden, während die Puffer geleert werden, erhalten Sie die Ausführung. Um dies nützlich zu machen, ändern Sie die Datei console.log in eine Rückrufmethode, die in Ihrem Hauptskript-Setup definiert ist, um Daten zu empfangen und darauf zu reagieren. Hoffe das hilft. Prost, Morgan.
quelle
Eine alternative Lösung besteht darin, den Job einer Warteschlange hinzuzufügen und ein Cron-Skript zu erstellen, das nach neuen Jobs sucht und diese ausführt.
Ich musste es kürzlich so machen, um die von einem gemeinsam genutzten Host auferlegten Grenzwerte zu umgehen - exec () et al. War für PHP, das vom Webserver ausgeführt wurde, deaktiviert, konnte aber in einem Shell-Skript ausgeführt werden.
quelle
Wenn die
flush()
Funktion nicht funktioniert. Sie müssen die nächsten Optionen in der php.ini wie folgt festlegen :quelle
Neueste Arbeitslösung
quelle
Nachdem ich viele verschiedene Lösungen aus diesem Thread ausprobiert habe (nachdem keine für mich funktioniert hat), habe ich auf der offiziellen PHP.net-Seite eine Lösung gefunden:
quelle