In-depth analysis of php execution after the browser exits

Source: Internet
Author: User
Tags flock
Will php continue execution after the browser exits? The following small editor will introduce whether php will continue to run after the browser exits. Let's take a look at the premise: Here is a typical lnmp structure, nginx + php-fpm mode

If I have a php program that runs very slowly or even sleep () in the code, a php-fpm process will be started when the browser connects to the service, but at this time, if the browser is closed, will the php-fpm process on the server continue to run at this time?

Today is to solve this problem.

Simplest experiment

The simplest way is to experiment. we write a program: Before and after sleep, we use file_put_contents to write logs:

<?phpfile_put_contents('/tmp/test.log', '11111' . PHP_EOL, FILE_APPEND | LOCK_EX);sleep(3);file_put_contents('/tmp/test.log', '2222' . PHP_EOL, FILE_APPEND | LOCK_EX);

The actual operation result is that when we close the client browser during server sleep, 2222 will be written into the log.

So it means that after the browser is closed, php on the server will continue to run?

Ignore_user_abort

Old Wang and diogin reminded that this may be related to php's ignore_user_abort function.

So I changed the code to something like this:

<?phpignore_user_abort(false);file_put_contents('/tmp/test.log', '11111' . PHP_EOL, FILE_APPEND | LOCK_EX);sleep(3);file_put_contents('/tmp/test.log', '2222' . PHP_EOL, FILE_APPEND | LOCK_EX);

It is found that there is no soft use, no matter what value ignore_user_abort is set, it will continue to be executed.

But here is a question: What is user_abort?

This document makes it clear to the abort in cli mode that when the php script is executed, the abort will be triggered when the user terminates the script. The script then judges whether to continue execution based on ignore_user_abort.

However, the official documents do not clearly describe the cgi mode abort. It seems that even if the client is disconnected, php in cgi mode will not receive abort.

Does ignore_user_abort play no role in cgi mode?

Is it heartbeat?

The first thing I think of is heartbeat? If we disconnect the browser client, it means that the client is disconnected without being closed. The server needs to wait for the tcp keepalive to arrive long before it can be detected.

Well, you must first eliminate the keepalive problem set by the browser.

Abandon the browser and simply write a client program: After the program connects to the http service, it sends a header, and sleeptakes one second to actively close the connection, and this program does not contain the http keepalive header.

The procedure is as follows:

package mainimport "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}

Server program:

<?phpignore_user_abort(false);file_put_contents('/tmp/test.log', '11111' . PHP_EOL, FILE_APPEND | LOCK_EX);sleep(3);file_put_contents('/tmp/test.log', '2222' . PHP_EOL, FILE_APPEND | LOCK_EX);

The results are still the same. php continues to execute the entire script, regardless of whether ignore_user_abort is set or not. It seems that ignore_user_abort still does not take effect.

How to trigger ignore_user_abort

How can I trigger ignore_user_abort? How does the server know that this socket cannot be used? Old Wang and diogin said whether the server needs to actively interact with the socket to determine whether the socket can be used?

In addition, we also found that php provides the connection_status and connection_aborted methods, both of which can detect the current connection status. So the line of code for logging can be changed:

file_put_contents('/tmp/test.log', '1 connection status: ' . connection_status() . "abort:" . connection_aborted() . PHP_EOL, FILE_APPEND | LOCK_EX);

According to the manual connection processing, we can print the current connection status.

There is still a program to interact with the socket. We use echo and remember to include flush later to eliminate the impact of flush.

The program is changed

<?phpignore_user_abort(true);file_put_contents('/tmp/test.log', '1 connection status: ' . 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('/tmp/test.log', '2 connection status: ' . connection_status() . "abort:" . connection_aborted(). PHP_EOL, FILE_APPEND | LOCK_EX);}

Very good. execute the client we wrote earlier. Observe the log:

1 connection status: 0abort:02 connection status: 0abort:02 connection status: 1abort:12 connection status: 1abort:12 connection status: 1abort:12 connection status: 1abort:12 connection status: 1abort:12 connection status: 1abort:12 connection status: 1abort:12 connection status: 1abort:12 connection status: 1abort:1

Finally, abort was created. The log also shows that the last few abort statuses are 1.

But here is a strange thing: why is the status of the first 2 connection status still 0 (NORMAL ).

RST

We use wireshark to capture packets and check the interaction process between the client and the server.

In this process, only 14 packets are sent. we can see that when the server sends 22222 for the first time, the client returns RST. No subsequent packet request is made.

As a result, the interaction process between the client and the server is:

When the server sends 2222 for the first time in a loop, the client returns an RST because the connection has been disconnected, but the request is successful. When the server tries to write data to the socket for the second time, the socket will not be transmitted over the network, and the connection status is abort. So the above situation occurs. for the first time, the status of 222 is 0, and for the second time, abort appears.

Verify strace

We can also use strace php-s xxx for verification.

The strace log is as follows:

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: 0 abort: 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) = 9rt_sigprocmask (SIG_BLOCK, [CHLD], [], 8) = 0rt_sigaction (SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0rt_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: 0 abort: 0 \ n", 30) = 30 close (5) = 0rt_sigprocmask (SIG_BLOCK, [CHLD], [], 8) = 0rt_sigaction (SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0rt_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: 1 abort: 1 \ n ", 30) = 30 close (5) = 0rt_sigprocmask (SIG_BLOCK, [CHLD], [], 8) = 0rt_sigaction (SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0rt_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: 1 abort: 1 \ n ", 30) = 30 close (5 )... We can see where status changes from 0 to 1 .... Sendto (4, "22222", 5, 0, NULL, 0) = 5... write (5, "2 connection status: 0 abort: 0 \ n", 30) = 30 close (5) = 0rt_sigprocmask (SIG_BLOCK, [CHLD], [], 8) = 0rt_sigaction (SIGCHLD, NULL, {SIG_DFL, [], 0}, 8) = 0rt_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: 1 abort: 1 \ n ", 30) = 30 close (5)

The Broken pipe is displayed when 2222 is sent to the socket for the second time. This is what the program tells us that this socket can no longer be used. By the way, connection_status in php will be set to 1. Subsequent write operations will not be executed.

Summary

Under normal circumstances, if the client exception is introduced, the program on the server will continue to be executed until two interactions with IO are performed. The server finds that the client has been disconnected, and a user_abort will be triggered at this time. if ignore_user_abort is not set, the php-fpm program will be interrupted.

Now, the problem is fixed.

The above in-depth analysis of php will continue to execute after the browser exits. it is all the content shared by the editor. I hope to give you a reference and support.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.