Original link: http://www.cnblogs.com/yjf512/p/5362025.html
The premise: Here is the typical LNMP structure, the NGINX+PHP-FPM pattern.
If I have a PHP program executed very slowly, even in the Code of Sleep (), and then the browser connected to the service, will start a php-fpm process, but this time, if the browser is closed, then ask, this time the server PHP-FPM process will continue to run it?
Today is to solve this problem.
The simplest of experiments
The simplest way is to do the experiment, we write a program: Before and after sleep with file_put_contents to write the log:
<?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);
As a result of the actual operation, we shut down the client browser during the server sleep process, and 2222 is written to the log.
Does that mean that PHP on the server will continue to run after the browser is closed?
Ignore_user_abort
Lao Wang and Diogin reminded that this could be related to PHP's Ignore_user_abort function.
So I changed the code slightly to 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);
The discovery does not have any soft use, regardless of setting ignore_user_abort why value, will continue to execute.
But here's a question: what is User_abort?
The document's abort of the CLI mode is clear, and when the PHP script executes, the user terminates the script, triggering abort. The script then determines whether or not to continue execution according to Ignore_user_abort.
However, the official document does not describe the abort of the CGI mode clearly. Feel that PHP in CGI mode will not receive abort even if the client is disconnected.
Doesn't ignore_user_abort have any effect in CGI mode?
Is it a heartbeat problem?
The first thing to think about is not the heartbeat problem? When we disconnect the browser client, we disconnect the client without close, and the server waits for the TCP keepalive to arrive before it is detected.
OK, you need to exclude the keepalive problem of browser settings first.
Abandon the browser, simply write a client program: After the program connects to the HTTP service, send a header, sleep1 seconds active close connection, and this program does not have 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}
Service-Side 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 discovery still remains the same, and PHP continues to execute the entire script regardless of whether the ignore_user_abort is set. It seems that Ignore_user_abort is still not in effect.
How to Trigger Ignore_user_abort
So how do you trigger the Ignore_user_abort? Service side how to know this socket can not use it? Lao Wang and Diogin said it is not necessary to service the client and the socket to interact to determine whether the socket can be used?
In addition, we found that PHP provides two methods for Connection_status and connection_aborted, both of which can detect the current connection state. So we can change the line code of the log to:
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 shows that we can print out the status of the current connection.
There is also a lack of a program to interact with the socket, we use echo, and incidentally remember to bring flush, excluding the effect of flush.
The program is changed to:
<?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); 0; $i < 10; $i + +) {echo Span class= "hljs-string" > "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 well, execute the client we wrote earlier. Observation 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 made the abort. The log also shows that the abort status of the following several times is 1.
But here's a strange place, why the first 2 connection status is still 0 (NORMAL).
Rst
We use the Wireshark capture package to see the entire client-side interaction process
This entire process only sends 14 packets, we see the service side sends the first time to send 22222, the client returns is the RST. No subsequent package requests are made at the back.
As a result, the approximate interaction flow between the client and the server is:
When the server sends 2222 in the loop for the first time, the client is returned with an RST because it has been disconnected, but the sending process is a successful request. Until the second service side again want to write in this socket, the socket will not be transferred to the network, the direct return to say connection state has been abort. So there is the above situation, the first time 222 is the status of 0, the second time only appears abort.
Strace for verification
We can also use strace php-s xxx to verify
The entire process Strace log is as follows:
。。。Close(5) = 0Lstat("/tmp/test.log", {st_mode=s_ifreg|0644, st_size=49873651, ...}) = 0Open("/tmp/test.log", o_wronly| O_creat| O_append,0666) = 5Fstat(5, {st_mode=s_ifreg|0644, st_size=49873651, ...}) = 0Lseek(5,0, Seek_cur) = 0Lseek(5,0, Seek_cur) = 0Flock(5, LOCK_EX) = 0Write(5,"1 Connection status:0abort:0\n",30) = 30Close(5) = 0SendTo(4,"Http/1.0 Ok\r\nconnection:clo" ...,89,0, NULL,0) = 89SendTo(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) = 0Nanosleep({3,0},0x7fff60a40290) = 0SendTo(4,"22222",5,0, NULL,0) = 5Open("/tmp/test.log", o_wronly| O_creat| O_append,0666) = 5Fstat(5, {st_mode=s_ifreg|0644, st_size=49873681, ...}) = 0Lseek(5,0, Seek_cur) = 0Lseek(5,0, Seek_cur) = 0Flock(5, LOCK_EX) = 0Write(5,"2 Connection status:0abort:0\n",30) = 30Close(5) = 0Rt_sigprocmask(Sig_block, [chld], [],8) = 0Rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [],0},8) = 0Rt_sigprocmask(Sig_setmask, [], NULL,8) = 0Nanosleep({1,0},0x7fff60a40290) = 0SendTo(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) = 5Fstat(5, {st_mode=s_ifreg|0644, st_size=49873711, ...}) = 0Lseek(5,0, Seek_cur) = 0Lseek(5,0, Seek_cur) = 0Flock(5, LOCK_EX) = 0Write(5,"2 Connection status:1abort:1\n",30) = 30Close(5) = 0Rt_sigprocmask(Sig_block, [chld], [],8) = 0Rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [],0},8) = 0Rt_sigprocmask(Sig_setmask, [], NULL,8) = 0Nanosleep({1,0},0x7fff60a40290) = 0Open("/tmp/test.log", o_wronly| O_creat| O_append,0666) = 5fstat ( 5, {st_mode=s_ifreg| 0644, St_size=49873741, ...}) = 0lseek (5, 0, seek_cur) = 0lseek (5, 0, seek_cur) = 0flock (5, lock_ex) = 0write (5, " 2 connection status:1abort:1\n ", 30) = 30close (5) ...
We see where the status shifts from 0 to 1.
...SendTo(4,"22222",5,0, NULL,0) = 5 ...Write(5,"2 Connection status:0abort:0\n",30) = 30Close(5) = 0Rt_sigprocmask(Sig_block, [chld], [],8) = 0Rt_sigaction(SIGCHLD, NULL, {SIG_DFL, [],0},8) = 0Rt_sigprocmask(Sig_setmask, [], NULL,8) = 0Nanosleep({1,0},0x7fff60a40290) = 0SendTo(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) = 5fstat (5, {St_mode=s_ifreg|0644, St_size=49873711, ...}) = 0lseek (5, 0, seek_cur) = 0lseek (5, 0, seek_cur) = 0flock (5, lock_ex) = 0write (5, " 2 connection status:1abort:1\n ", 30) = 30close (5) = 0
The second time I send 2222 to the socket, it shows the broken pipe. This is the program tells us that the socket is no longer available, and the PHP connection_status will be set to 1. Subsequent write operations are no longer performed.
Summarize
Under normal circumstances, if the client clients are thrown out unexpectedly, the server's program will continue to execute until it interacts with IO two times. Server discovery client has been disconnected, this time will trigger a user_abort, if this is not set Ignore_user_abort, then this PHP-FPM program will be interrupted.
At this point, the problem is closed.
[to] will PHP continue to execute after the browser exits?