Nginx和PHP-FPM之间的通信可以采用TCP网络通信,或者是更加轻量但高并发没有TCP稳定的UnixSock(不需要经过网络),下面以TCP通信说明. tcpdump/wireshark分析firefox/nginx/php-fpm完成一次PHP请求 sudo lsof -i -n -P | egrep ":80|:9000" | grep ESTABLISHED firefox 2510 eechen 69u IPv4 288893 0t0 TCP 127.0.0.1:57939->127.0.0.1:80 (ESTABLISHED) nginx 6843 png 3u IPv4 286299 0t0 TCP 127.0.0.1:80->127.0.0.1:57939 (ESTABLISHED) nginx 6843 png 17u IPv4 286300 0t0 TCP 127.0.0.1:58518->127.0.0.1:9000 (ESTABLISHED) php-fpm 6865 png 3u IPv4 288877 0t0 TCP 127.0.0.1:9000->127.0.0.1:58518 (ESTABLISHED) firefox通过端口57939访问nginx的80端口. nginx则通过端口58518访问php-fpm的9000端口. ESTABLISHED表示连接是keep-alive的. WireShark中监听lo设备或者用tcpdump抓包分析: sudo tcpdump -s 0 -i lo -w data.pcap port 80 or 9000 WireShark里可以用下面的Filter进行过滤分析: tcp.port==57939 or tcp.port==58518 57939 > http [SYN] http > 57939 [SYN, ACK] 57939 > http [ACK] 上面三个包代表firefox和nginx的三次握手. GET /app/buffer.php HTTP/1.1 http > 57939 [ACK] 这两个包代表firefox向nginx请求一个URL. 58518 > cslistener [SYN] cslistener > 58518 [SYN, ACK] 58518 > cslistener [ACK] 上面三个包代表nginx和php-fpm的三次握手. 58518 > cslistener [PSH, ACK] cslistener > 58518 [ACK] 上面两个包代表nginx将firefox的请求转发给php-fpm. cslistener > 58518 [PSH, ACK] php-fpm向nginx推送数据 58518 > cslistener [ACK] nginx收到数据传输给firefox [TCP segment of a reassembled PDU] 57939 > http [ACK] firefox收到数据 cslistener > 58518 [PSH, ACK] php-fpm继续向nginx推送数据,进入循环. cslistener > 58518 [FIN, ACK] php-fpm关闭响应 58518 > cslistener [FIN, ACK] nginx关闭响应 cslistener > 58518 [ACK] php-fpm响应 HTTP/1.1 200 OK (text/html) 57939 > http [ACK] firefox响应 buffer.php测试代码: <?php ignore_user_abort(true); set_time_limit(0); ob_end_clean(); ob_start(); for($i=10;$i>0;$i--){ echo date('H:i:s').'<br />'; echo str_repeat(' ', 1024*4); ob_flush(); flush(); sleep(1); } echo 'Stop.'; ob_end_flush(); 另外注意: Nginx的gzip可能会进行输出缓存,这将导致flush()函数产生的结果不会立即被发送到客户端浏览器.在Nginx+PHP-FPM下还要注意Nginx的fastcgi buffer,比如: fastcgi_buffer_size 128k; fastcgi_buffers 8 128k; 表示Nginx会缓冲PHP-FPM输出的信息,当达到128k时才会将缓冲区的数据发送给客户端,那么我们首先需要将这个缓冲区调小: fastcgi_buffer_size 4k; fastcgi_buffers 8 4k; 并且,必须禁用gzip: gzip off; 然后,在php中,在ob_flush和flush前,输出一段达到4k的内容,例如: echo str_repeat(' ', 1024*4); 到此,PHP就可以正常通过ob_flush和flush逐行输出需要的内容了.
Nginx和PHP-FPM之间的通信可以采用TCP网络通信,或者是更加轻量但高并发没有TCP稳定的UnixSock(不需要经过网络),下面以TCP通信说明.
tcpdump/wireshark分析firefox/nginx/php-fpm完成一次PHP请求

sudo lsof -i -n -P | egrep ":80|:9000" | grep ESTABLISHED
firefox通过端口57939访问nginx的80端口.
nginx则通过端口58518访问php-fpm的9000端口.
ESTABLISHED表示连接是keep-alive的.
WireShark中监听lo设备或者用tcpdump抓包分析:
sudo tcpdump -s 0 -i lo -w data.pcap port 80 or 9000
WireShark里可以用下面的Filter进行过滤分析:
tcp.port==57939 or tcp.port==58518
57939 > http [SYN]
http > 57939 [SYN, ACK]
57939 > http [ACK]
上面三个包代表firefox和nginx的三次握手.
GET /app/buffer.php HTTP/1.1
http > 57939 [ACK]
这两个包代表firefox向nginx请求一个URL.
58518 > cslistener [SYN]
cslistener > 58518 [SYN, ACK]
58518 > cslistener [ACK]
上面三个包代表nginx和php-fpm的三次握手.
58518 > cslistener [PSH, ACK]
cslistener > 58518 [ACK]
上面两个包代表nginx将firefox的请求转发给php-fpm.
cslistener > 58518 [PSH, ACK] php-fpm向nginx推送数据
58518 > cslistener [ACK] nginx收到数据传输给firefox
[TCP segment of a reassembled PDU]
57939 > http [ACK] firefox收到数据
cslistener > 58518 [PSH, ACK] php-fpm继续向nginx推送数据,进入循环.
cslistener > 58518 [FIN, ACK] php-fpm关闭响应
58518 > cslistener [FIN, ACK] nginx关闭响应
cslistener > 58518 [ACK] php-fpm响应
HTTP/1.1 200 OK (text/html)
57939 > http [ACK] firefox响应
buffer.php测试代码:
另外注意: Nginx的gzip可能会进行输出缓存,这将导致flush()函数产生的结果不会立即被发送到客户端浏览器.在Nginx+PHP-FPM下还要注意Nginx的fastcgi buffer,比如:
fastcgi_buffer_size 128k;
fastcgi_buffers 8 128k;
表示Nginx会缓冲PHP-FPM输出的信息,当达到128k时才会将缓冲区的数据发送给客户端,那么我们首先需要将这个缓冲区调小:
fastcgi_buffer_size 4k;
fastcgi_buffers 8 4k;
并且,必须禁用gzip:
gzip off;
然后,在php中,在ob_flush和flush前,输出一段达到4k的内容,例如:
echo str_repeat(' ', 1024*4);
到此,PHP就可以正常通过ob_flush和flush逐行输出需要的内容了.