您的位置:首页 > 运维架构 > Nginx

nginx与PHP的flush(摘抄加总结)

2015-11-07 20:36 555 查看
先看一段代码呗,它的作用是间断性输出

for ($i = 0; $i < 10; $i++) {
echo $i.'<br />';
flush();
sleep(1);
}


据说apache下是可以的。但nginx下一定不行,nginx下正确步骤如下

1.首先需要设置nginx.conf:

fastcgi_buffer_size 4k; //可以放的位置为 http、server、location
fastcgi_buffers 64 4k; //可以放的位置为 http、server、location
gzip off; //我只看到过在server段配置


记得重启php和nginx

2.代码部分

<?php
ob_start();
for ($i = 0; $i < 10; $i++) {
echo $i.'<br />';
echo str_repeat(" ", 1024*4);
ob_flush();
flush();
sleep(1);
}
?>


然后就可以1s输出一行了

首先看下我查到的资料

先说php缓冲:php.ini 中的 output_buffering

1.buffer

buffer是一个内存地址空间,Linux系统默认大小一般为4096(4kb),即一个内存页,可以在终端执行
getconf PAGESIZE
来查看。主要用于存储速度不同步的设备或者优先级不同的设备之间处理数据的区域。通过buffer,可以使进程之间的相互等待变少。这里说一个通俗一点的例子,你打开文本编辑器编辑一个文件的时候,你每输入一个字符,操作系统并不会立即把这个字符直接写入到磁盘,而是先写入到buffer,当写满了一个buffer的时候,才会把buffer中的数据写入磁盘,当然当调用内核函数flush()的时候,强制要求把buffer中的脏数据写回磁盘。

同样的道理,当执行echo、print的时候,输出并没有立即通过tcp传给客户端浏览器显示,而是将数据写入php buffer。php output_buffering机制,意味在tcp buffer之前,建立了一新的队列,数据必须经过该队列。当一个php buffer写满的时候,脚本进程会将php buffer中的输出数据交给系统内核交由tcp传给浏览器显示。所以,数据会依次写到这几个地方:echo/print -> php buffer -> tcp buffer -> browser

2.php output_buffering

默认情况下,php buffer是开启的,而且该buffer默认值是4096,即4kb。你可以通过在php.ini配置文件中找到output_buffering配置。当echo、print等输出用户数据的时候,输出数据都会写入到php output_buffering中,直到output_buffering写满,会将这些数据通过tcp传送给浏览器显示。你也可以通过ob_start()手动激活php output_buffering机制,使得即便输出超过了4kb数据,也不真的把数据交给tcp传给浏览器,因为ob_start()将php buffer空间设置到了足够大。只有直到脚本结束,或者调用ob_end_flush函数,才会把数据发送给客户端浏览器。

(1)当output_buffering=4096,并且输出较少数据(少于一个buffer)

<?php
for ($i = 0; $i < 10; $i++) {
echo $i . '<br/>';
sleep($i + 1); //
}
?>


现象:不是每隔几秒就会有间断性输出,而是直到响应结束,才能一次性看到输出,在等待服务器脚本处理结束之前,浏览器界面一直保持空白。这是因为,数据量太小,php output_buffering没有写满。写数据的顺序,依次是echo->php buffer->tcp buffer->browser

(2)当output_buffering=0,并且输出较少数据(少于一个buffer)

<?php
//通过ini_set('output_buffering', 0)并不生效
//应该编辑/etc/php.ini,设置output_buffering=0禁用output buffering机制
//ini_set('output_buffering', 0); //彻底禁用output buffering功能
for ($i = 0; $i < 10; $i++) {
echo $i . '<br/>';
flush(); //通知操作系统底层,尽快把数据给客户端浏览器
sleep($i + 1); //
}
?>


现象:与刚才显示并不一致,禁用了php buffering机制之后,在浏览器可以断断续续看到间断性输出,不必等到脚本执行完毕才看到输出。这是因为,数据没有在php output_buffering中停留。写数据的顺序依次是echo->tcp buffer->browser

(3)当output_buffering=4096,输出数据大于一个buffer,不调用ob_start()

#//创建一个4kb大小的文件
$dd if=/dev/zero of=f4096 bs=4096 count=1
<?php
for ($i = 0; $i < 10; $i++) {
echo file_get_contents('./f4096') . $i . '<br/>';
sleep($i +1);
}
?>


这里我说明下,第一行代码是在命令行创建一个4096大小的文件,然后每次输出
$i
之前现输出这个文件内容,作用就是叫当前echo的内容大于output_buffering设置的值这样就达到输出数据大于一个buffer的效果了。


现象:响应还没结束(http连接没有关闭),断断续续可以看到间断性输出,浏览器界面不会一直保持空白。尽管启用了php output_buffering机制,但依然会间断性输出,而不是一次性输出,是因为output_buffering空间不够用。每写满一个php buffering,数据就会发送到客户端浏览器。

(4)当output_buffering=4096, 输出数
4000
据大于一个tcp buffer, 调用ob_start()

现象:直到服务端脚本处理完成,响应结束,才看到完整输,输出间隔时间很短,以至你感受不到停顿。在输出之前,浏览器一直保持着空白界面,等待服务端数据。这是因为,php一旦调用了ob_start()函数,它会将php buffer扩展到足够大,直到ob_end_flush函数调用或者脚本运行结速才发送php buffer中的数据到客户端浏览器。

<?php
ob_start(); //开启php buffer
for ($i = 0; $i < 10; $i++) {
echo file_get_contents('./f4096') . $i . '<br/>';
sleep($i + 1);
}
ob_end_flush();
?>


现象:直到服务端脚本处理完成,响应结束,才看到完整输,输出间隔时间很短,以至你感受不到停顿。在输出之前,浏览器一直保持着空白界面,等待服务端数据。这是因为,php一旦调用了ob_start()函数,它会将php buffer扩展到足够大,直到ob_end_flush函数调用或者脚本运行结速才发送php buffer中的数据到客户端浏览器。

然后笔者看到这里,觉得看懂了output_buffering 也明白了如何实现间断性输出的特效然后就试了下开头部分的第一段代码结果不行。经过多方百度才得写出了第二段代码并实现了效果。所以,以上文字是建立在apache的基础上才有的

文章我抄到这里,原文还有一些其他的东西,在这就不继续赘述了,因为还有别的事,原文地址如下

http://www.jb51.net/article/38363.htm

说到这来叙述下我的开发环境

MAC OX 10.11.1 + NGINX 1.8.0+ PHP 5.6+ PHP-FPM + MYSQL**

现在开始叙述下我的编码过程

一开始

<?php
for ($i = 0; $i < 10; $i++) { echo $i.'<br />'; flush(); sleep(1); }
?>


这段代码肯定是不行的,然后就百度,有人说linux环境下
flush()
要和
ob_flush()
配合使用,于是有了下面的代码


<?php
for ($i = 0; $i < 10; $i++) {
echo $i.'<br />';
ob_flush();
flush();
sleep(1);
}
?>


可是还是不行,当然现在才明白,他一定是linux下apache,我mac下nginx,业界良心,在记录问题时应该说下自己的开发环境。然后继续百度当时感觉和nginx有关就带上了nginx关键字:flush在nginx下失效 和 nginx flush 分别进行搜索

然后读到了一篇文章,现摘抄如下

nginx与PHP的flush

原文地址 http://www.99n9.com/blog/?p=257

Nginx与php-cgi是两个独立的程序,通过TCP或Unix套接字通信,不像Apache那样是集成在一起的。所以,Nginx 有 fastcgi

缓冲区,数据超出缓冲区大小、或程序执行完,才会将内容输出到客户端。另外,如果要使用ob_flush,不能开启gzip压缩输出。

nginx.conf:
fastcgi_buffer_size 1k;
fastcgi_buffers 4 1k;
gzip off;


php.ini:

改为
output_buffering = Off


如果需要连续循环打印的效果,可以加上一行
echo str_repeat("",1024);
,使得fastcgi_buffer_size 1k;的缓冲区满,从而输出内容到浏览器:

<?php
set_time_limit(0);
ob_end_clean();
ob_implicit_flush(1);

for($i = 0; $i < 10; $i++){
echo $i, “<br />n”;
echo str_repeat(” “,1024);
sleep(1);
}


看到这后,似乎终于明白了什么,于是乎没有理他的代码,在自己的代码上改进,然后修改nginx配置,又出了一版

<?php
for ($i = 0; $i < 10; $i++) {
echo $i.'<br />';
echo str_repeat(" ", 1024*4);
ob_flush();
flush();
sleep(1);
}
?>


确实是间断性输出了但是抱错:
ob_flush(): failed to flush buffer.


然后还是百度,最后在代码开头加上
ob_start();
就好了。


这就完了?为了出效果算是完了,但是我自己又做了一些尝试。

设置output_buffering

output_buffering = 4096


发现代码执行没有影响,难道说nginx环境下,经过以上的配置后可以无视output_buffering 吗?

先记录下来,然后择日再看关于缓冲这块 可能会写出更正确的代码吧
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  nginx buffer