Heim Backend-Entwicklung PHP-Tutorial Eine eingehende Untersuchung, ob PHP nach dem Beenden des Browsers weiterhin ausgeführt wird

Eine eingehende Untersuchung, ob PHP nach dem Beenden des Browsers weiterhin ausgeführt wird

Dec 29, 2017 pm 06:56 PM
firefox webkit 浏览器

Wird PHP nach dem Beenden des Browsers weiterhin ausgeführt? Der folgende Editor zeigt Ihnen, ob PHP nach dem Beenden des Browsers weiterhin ausgeführt wird. Ich hoffe, es hilft allen.

Prämisse: Worüber wir hier sprechen, ist die typische LNMP-Struktur, Nginx+php-fpm-Modus

Wenn ich ein PHP-Programm habe, das sehr langsam ausgeführt wird, sogar sleep() im Code Wenn der Browser dann eine Verbindung zum Dienst herstellt, wird ein PHP-FPM-Prozess gestartet. Wenn der Browser jedoch zu diesem Zeitpunkt geschlossen wird, wird der PHP-FPM-Prozess auf dem Server zu diesem Zeitpunkt weiterhin ausgeführt?

Heute werden wir dieses Problem lösen.

Das einfachste Experiment

Der einfachste Weg ist, ein Experiment durchzuführen: Verwenden Sie file_put_contents vor und nach dem Schlafen das Protokoll:

<?php
file_put_contents(&#39;/tmp/test.log&#39;, &#39;11111&#39; . PHP_EOL, FILE_APPEND | LOCK_EX);
sleep(3);
file_put_contents(&#39;/tmp/test.log&#39;, &#39;2222&#39; . PHP_EOL, FILE_APPEND | LOCK_EX);
Nach dem Login kopieren

Das Ergebnis des eigentlichen Vorgangs ist, dass 2222 in das Protokoll geschrieben wird, wenn wir den Client-Browser schließen, während der Server schläft.

Das bedeutet also, dass nach dem Schließen des Browsers das serverseitige PHP weiterhin ausgeführt wird?

ignore_user_abort

Lao Wang und Diogin erinnerten daran, dass dies möglicherweise damit zusammenhängt Bezogen auf die Funktion „ignore_user_abort“ von PHP.

Also habe ich den Code leicht geändert, so dass er so aussieht:

<?php
ignore_user_abort(false);
file_put_contents(&#39;/tmp/test.log&#39;, &#39;11111&#39; . PHP_EOL, FILE_APPEND | LOCK_EX);
sleep(3);
file_put_contents(&#39;/tmp/test.log&#39;, &#39;2222&#39; . PHP_EOL, FILE_APPEND | LOCK_EX);
Nach dem Login kopieren
Nach dem Login kopieren

Ich habe festgestellt, dass keine Software verwendet wird, egal auf welchen Wert „ignore_user_abort“ eingestellt ist, die Ausführung wird fortgesetzt.

Aber hier ist eine Frage: Was ist user_abort?

Das Dokument macht den Abbruch im CLI-Modus sehr deutlich. Wenn das PHP-Skript ausgeführt wird und der Benutzer das Skript beendet, wird ein Abbruch ausgelöst. Das Skript bestimmt dann anhand von „ignore_user_abort“, ob die Ausführung fortgesetzt werden soll.

Aber das offizielle Dokument beschreibt den Abbruch im CGI-Modus nicht eindeutig. Es fühlt sich so an, als würde PHP im CGI-Modus keinen Abbruch erhalten, selbst wenn der Client die Verbindung trennt.

Hat „ignore_user_abort“ im CGI-Modus keine Auswirkung?

Ist es ein Herzschlagproblem?

Das erste, was mir in den Sinn kommt, ist ein Herzproblem? Wenn wir den Browser-Client trennen, entspricht dies dem Trennen der Verbindung, ohne den Client zu schließen. Der Server muss auf das Eintreffen des TCP-Keepalive warten, bevor er es erkennt.

Okay, Sie müssen zuerst das Keepalive-Problem in den Browsereinstellungen beseitigen.

Verlassen Sie den Browser und schreiben Sie einfach ein Client-Programm: Nachdem das Programm eine Verbindung zum HTTP-Dienst hergestellt hat, sendet es einen Header und schläft eine Sekunde lang, bevor es die Verbindung aktiv schließt. Dieses Programm verfügt jedoch nicht über das HTTP-Keepalive Kopfzeile.

Das Programm ist wie folgt:

package main

import "net"
import "fmt"
import "time"

func main() {
  conn, _ := net.Dial("tcp", "192.168.33.10:10011")
  fmt.Fprintf(conn, "GET /index.php HTTP/1.0\r\n\r\n")
  time.Sleep(1 * time.Second)
  conn.Close()
  return
}
Nach dem Login kopieren

Serverprogramm:

<?php
ignore_user_abort(false);
file_put_contents(&#39;/tmp/test.log&#39;, &#39;11111&#39; . PHP_EOL, FILE_APPEND | LOCK_EX);
sleep(3);
file_put_contents(&#39;/tmp/test.log&#39;, &#39;2222&#39; . PHP_EOL, FILE_APPEND | LOCK_EX);
Nach dem Login kopieren
Nach dem Login kopieren

Es wurde festgestellt, dass es immer noch dasselbe ist, PHP funktioniert weiterhin, unabhängig davon, ob „ignore_user_abort“ festgelegt ist oder nicht. Setzen Sie die Ausführung fort, um das gesamte Skript abzuschließen. Es scheint, dass „ignore_user_abort“ immer noch keine Wirkung zeigt.

Wie löst man „ignore_user_abort“ aus?

Wie löst man „ignore_user_abort“ aus? Woher weiß der Server, dass dieser Socket nicht verwendet werden kann? Lao Wang und Diogin fragten, ob der Server aktiv mit dem Socket interagieren muss, um festzustellen, ob der Socket verwendet werden kann.

Darüber hinaus haben wir festgestellt, dass PHP zwei Methoden bereitstellt: „connection_status“ und „connection_aborted“, die beide den aktuellen Verbindungsstatus erkennen können. Daher kann unsere Protokollierungscodezeile wie folgt geändert werden:

file_put_contents(&#39;/tmp/test.log&#39;, &#39;1 connection status: &#39; 
. connection_status() 
. "abort:" 
. connection_aborted() 
. PHP_EOL, FILE_APPEND | LOCK_EX);
Nach dem Login kopieren

Gemäß der manuellen Verbindungsverarbeitung können wir den aktuellen Verbindungsstatus ausdrucken.

Im Folgenden fehlt noch ein Programm, das mit dem Socket interagiert. Wir verwenden Echo und denken daran, Flush später zu bringen, um die Auswirkungen von Flush zu beseitigen.

Das Programm wurde geändert in

<?php
ignore_user_abort(true);
file_put_contents(&#39;/tmp/test.log&#39;, &#39;1 connection status: &#39; . connection_status() . "abort:" . connection_aborted() . PHP_EOL, FILE_APPEND | LOCK_EX);
sleep(3);
for($i = 0; $i < 10; $i++) {
    echo "22222";
    flush();
    sleep(1);
    file_put_contents(&#39;/tmp/test.log&#39;, &#39;2 connection status: &#39; . connection_status() . "abort:" . connection_aborted(). PHP_EOL, FILE_APPEND | LOCK_EX);
}
Nach dem Login kopieren

Sehr gut, führen Sie den Client aus, den wir zuvor geschrieben haben. Beobachtungsprotokoll:


1 connection status: 0abort:0
2 connection status: 0abort:0
2 connection status: 1abort:1
2 connection status: 1abort:1
2 connection status: 1abort:1
2 connection status: 1abort:1
2 connection status: 1abort:1
2 connection status: 1abort:1
2 connection status: 1abort:1
2 connection status: 1abort:1
2 connection status: 1abort:1
Nach dem Login kopieren

Endlich wurde ein Abbruch erstellt. Das Protokoll zeigt auch, dass der Abbruchstatus für die nächsten Male 1 ist.

Aber hier gibt es etwas Seltsames, warum ist der Status der ersten 2 Verbindungsstatus immer noch 0 (NORMAL).

RST

Wir verwenden Wireshark, um Pakete zu erfassen und den gesamten Interaktionsprozess zwischen dem Client und dem Server zu sehen

Dies Der gesamte Prozess sendet nur 14 Pakete. Sehen wir uns an, dass der Client RST zurückgibt, wenn der Server zum ersten Mal 22222 sendet. Es erfolgen keine weiteren Paketanfragen.

Ich verstehe also, dass der ungefähre Interaktionsprozess zwischen dem Client und dem Server wie folgt aussieht:

Wenn der Server zum ersten Mal in der Schleife 2222 sendet, hat der Client die Verbindung getrennt. Was zurückgegeben wird, ist ein RST, dieser Sendevorgang wird jedoch als erfolgreiche Anfrage gewertet. Bis der Server zum zweiten Mal erneut einen Schreibvorgang für diesen Socket ausführen möchte, führt dieser Socket keine Netzwerkübertragung durch und gibt direkt zurück, dass der Verbindungsstatus abgebrochen ist. Die obige Situation trat also beim ersten Mal auf, als 222 den Status 0 hatte, und der Abbruch trat erst beim zweiten Mal auf.

strace zur Überprüfung

Wir können zur Überprüfung auch strace php -S XXX verwenden

Das Strace-Protokoll des gesamten Prozesses lautet wie folgt:

close(5)                = 0
lstat("/tmp/test.log", {st_mode=S_IFREG|0644, st_size=49873651, ...}) = 0
open("/tmp/test.log", O_WRONLY|O_CREAT|O_APPEND, 0666) = 5
fstat(5, {st_mode=S_IFREG|0644, st_size=49873651, ...}) = 0
lseek(5, 0, SEEK_CUR)          = 0
lseek(5, 0, SEEK_CUR)          = 0
flock(5, LOCK_EX)            = 0
write(5, "1 connection status: 0abort:0\n", 30) = 30
close(5)                = 0
sendto(4, "HTTP/1.0 200 OK\r\nConnection: clo"..., 89, 0, NULL, 0) = 89
sendto(4, "111111111", 9, 0, NULL, 0)  = 9
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({3, 0}, 0x7fff60a40290)    = 0
sendto(4, "22222", 5, 0, NULL, 0)    = 5
open("/tmp/test.log", O_WRONLY|O_CREAT|O_APPEND, 0666) = 5
fstat(5, {st_mode=S_IFREG|0644, st_size=49873681, ...}) = 0
lseek(5, 0, SEEK_CUR)          = 0
lseek(5, 0, SEEK_CUR)          = 0
flock(5, LOCK_EX)            = 0
write(5, "2 connection status: 0abort:0\n", 30) = 30
close(5)                = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({1, 0}, 0x7fff60a40290)    = 0
sendto(4, "22222", 5, 0, NULL, 0)    = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=2819, si_uid=0} ---
open("/tmp/test.log", O_WRONLY|O_CREAT|O_APPEND, 0666) = 5
fstat(5, {st_mode=S_IFREG|0644, st_size=49873711, ...}) = 0
lseek(5, 0, SEEK_CUR)          = 0
lseek(5, 0, SEEK_CUR)          = 0
flock(5, LOCK_EX)            = 0
write(5, "2 connection status: 1abort:1\n", 30) = 30
close(5)                = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({1, 0}, 0x7fff60a40290)    = 0
open("/tmp/test.log", O_WRONLY|O_CREAT|O_APPEND, 0666) = 5
fstat(5, {st_mode=S_IFREG|0644, st_size=49873741, ...}) = 0
lseek(5, 0, SEEK_CUR)          = 0
lseek(5, 0, SEEK_CUR)          = 0
flock(5, LOCK_EX)            = 0
write(5, "2 connection status: 1abort:1\n", 30) = 30
close(5) 
。。。我们照中看status从0到1转变的地方。
...
sendto(4, "22222", 5, 0, NULL, 0)    = 5
...
write(5, "2 connection status: 0abort:0\n", 30) = 30
close(5)                = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
nanosleep({1, 0}, 0x7fff60a40290)    = 0
sendto(4, "22222", 5, 0, NULL, 0)    = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=2819, si_uid=0} ---
open("/tmp/test.log", O_WRONLY|O_CREAT|O_APPEND, 0666) = 5
fstat(5, {st_mode=S_IFREG|0644, st_size=49873711, ...}) = 0
lseek(5, 0, SEEK_CUR)          = 0
lseek(5, 0, SEEK_CUR)          = 0
flock(5, LOCK_EX)            = 0
write(5, "2 connection status: 1abort:1\n", 30) = 30
close(5)
Nach dem Login kopieren

第二次往socket中发送2222的时候显示了Broken pipe。这就是程序告诉我们,这个socket已经不能使用了,顺便php中的connection_status就会被设置为1了。后续的写操作也都不会再执行了。

总结

正常情况下,如果客户端client异常推出了,服务端的程序还是会继续执行,直到与IO进行了两次交互操作。服务端发现客户端已经断开连接,这个 时候会触发一个user_abort,如果这个没有设置ignore_user_abort,那么这个php-fpm的程序才会被中断。

至此,问题结了。

相关推荐:

PHP 底层的运行机制与原理

php cgi与fpm关系

PHP简单实现socks5代理服务器

Das obige ist der detaillierte Inhalt vonEine eingehende Untersuchung, ob PHP nach dem Beenden des Browsers weiterhin ausgeführt wird. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

AI Hentai Generator

AI Hentai Generator

Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

R.E.P.O. Energiekristalle erklärten und was sie tun (gelber Kristall)
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Beste grafische Einstellungen
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. So reparieren Sie Audio, wenn Sie niemanden hören können
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Wie man alles in Myrise freischaltet
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Wie kann man das Größensymbol durch CSS anpassen und es mit der Hintergrundfarbe einheitlich machen? Wie kann man das Größensymbol durch CSS anpassen und es mit der Hintergrundfarbe einheitlich machen? Apr 05, 2025 pm 02:30 PM

Die Methode zur Anpassung der Größe der Größe der Größe der Größe in CSS ist mit Hintergrundfarben einheitlich. In der täglichen Entwicklung begegnen wir häufig Situationen, in denen wir die Details der Benutzeroberfläche wie Anpassung anpassen müssen ...

Wie kann man Lückeneffekt auf das Karten- und Gutscheinlayout mit Gradientenhintergrund erzielen? Wie kann man Lückeneffekt auf das Karten- und Gutscheinlayout mit Gradientenhintergrund erzielen? Apr 05, 2025 am 07:48 AM

Erkennen Sie den Lückeneffekt des Karten -Gutschein -Layouts. Beim Entwerfen von Karten -Gutschein -Layout begegnen Sie häufig die Notwendigkeit, Lücken zu Karten -Gutscheinen hinzuzufügen, insbesondere wenn der Hintergrund Gradient ist ...

Wie zeige ich die lokal installierte 'Jingnan Mai Round Body' auf der Webseite richtig? Wie zeige ich die lokal installierte 'Jingnan Mai Round Body' auf der Webseite richtig? Apr 05, 2025 pm 10:33 PM

Mit lokal installierten Schriftdateien auf Webseiten kürzlich habe ich eine kostenlose Schriftart aus dem Internet heruntergeladen und sie erfolgreich in mein System installiert. Jetzt...

Warum wirkt sich negative Margen in einigen Fällen nicht wirksam? Wie löst ich dieses Problem? Warum wirkt sich negative Margen in einigen Fällen nicht wirksam? Wie löst ich dieses Problem? Apr 05, 2025 pm 10:18 PM

Warum werden negative Margen in einigen Fällen nicht wirksam? Während der Programmierung negative Margen in CSS (negativ ...

Der Text unter Flex -Layout wird weggelassen, aber der Behälter wird geöffnet? Wie löst ich es? Der Text unter Flex -Layout wird weggelassen, aber der Behälter wird geöffnet? Wie löst ich es? Apr 05, 2025 pm 11:00 PM

Das Problem der Containeröffnung aufgrund einer übermäßigen Auslassung von Text unter Flex -Layout und Lösungen werden verwendet ...

Wie erhalten Sie Echtzeit-Anwendungs- und Zuschauerdaten auf der Arbeit von 58.com? Wie erhalten Sie Echtzeit-Anwendungs- und Zuschauerdaten auf der Arbeit von 58.com? Apr 05, 2025 am 08:06 AM

Wie erhalte ich dynamische Daten von 58.com Arbeitsseite beim Kriechen? Wenn Sie eine Arbeitsseite von 58.com mit Crawler -Tools kriechen, können Sie auf diese begegnen ...

Wie verwendet ich CSS und Flexbox, um das reaktionsschnelle Layout von Bildern und Text in verschiedenen Bildschirmgrößen zu implementieren? Wie verwendet ich CSS und Flexbox, um das reaktionsschnelle Layout von Bildern und Text in verschiedenen Bildschirmgrößen zu implementieren? Apr 05, 2025 pm 06:06 PM

Implementieren von Responsive Layouts mit CSS, wenn wir Layoutänderungen unter verschiedenen Bildschirmgrößen im Webdesign, CSS ...

Warum wird ein bestimmtes Div -Element im Edge -Browser nicht angezeigt? Wie löst ich dieses Problem? Warum wird ein bestimmtes Div -Element im Edge -Browser nicht angezeigt? Wie löst ich dieses Problem? Apr 05, 2025 pm 08:21 PM

Wie löst ich das durch User Agent Style Sheets verursachte Anzeigeproblem? Bei Verwendung des Edge -Browsers kann ein Div -Element im Projekt nicht angezeigt werden. Nachdem ich nachgesehen hatte, habe ich gepostet ...

See all articles