php 缓冲区,PHP输出缓冲(output buffer)初级理解和应用

声明:不同的PHP版本,操作系统环境,Webserver, 参数默认值及配置可能会有差异。本文基于的环境如下:

PHP version: 7.2.8; Nginx version 1.14.0

Mac OS version: 10.10.5; Chrome version: 69.0;

Safari version: 8.0.8第一部分 基础概念

计算机缓冲区(buffer)

计算机中暂时存放输出或输入信息的内存区域。缓和高速部件和低速部件之间通信速度不匹配的矛盾。缓冲(buffer)和缓存(cache)之间还是有本质区别的【点击查看区别】。

PHP输出缓冲区

输出缓冲区顾名思义是输出信息暂时存放的内存区域,通过ob_*系列函数来控制输出缓冲区。当php脚本执行结束(会自动调用ob_flush())或强制刷新(手动调用ob_fush())缓冲区后,才会把数据发送给Nginx fastcgi客户端。当然PHP还有其他的缓冲区,比如字符串缓冲区finfo::buffer。

PHP输出缓冲区默认是关闭的,可以在php.ini中开启,生产环境官方建议大小是4096字节。开启后对所有php页面都生效。

output_buffering = 4096

// 其他值说明

; On = Enabled and buffer is unlimited. (小心使用)

; Off = Disabled

; Integer = Enables the buffer and sets its maximum size in bytes.

; Note: This directive is hardcoded to Off for the CLI SAPI

另外一种在页面中单独开启缓冲区的办法是ob_start()函数。

bool ob_start ([ callback $output_callback [, int $chunk_size [, bool $erase ]]] )

ob_start()有三个参数,$chunk_size是用来设置缓冲区大小,可以设置0-4096,默认是0表示大小不限。

PHP-FPM缓冲区(暂时这么认为,如果读者有更好的建议,欢迎留言)

参考《PHP output buffer – bigpipe基础》。这篇文章提到认为是fpm缓冲区,但是官网没有明确说明,测试验证确实有8K的缓冲,通过flush()刷新。flush()函数严格来说, 这个只有在PHP做为Apache的Module安装的时候, 才有实际作用,它是刷新WebServer(可以认为特指Apache)的缓冲区。参考鸟哥文章深入理解flush和ob_flush区别。所以flush()并不能刷新Nginx fastcgi缓冲区。

Nginx缓冲区

Nginx默认不会实时把php-fpm响应的数据返回给客户端,而是暂存在Nginx缓冲区中。当php脚本执行结束(自动调用flush())或强制刷新(手动flush())缓冲区后,才会把数据发送给客户端。Nginx buffer有fastcgi_buffering和porxy_buffering两种。Nginx通过fastcgi_buffer*相关指令来控制fastcgi_buffering缓冲区。通过porxy_buffer*指令来控制porxy_buffering。

浏览器缓冲区

浏览器默认不会实时显示从Nginx返回的数据,而是把接受到的数据暂存在浏览器缓冲区中,当缓冲区满后,才会开始显示。不同的浏览器缓冲区大小不同。实际测试发现Mac 下chrome和safari都需要输出1024字节。没有找到刷新缓冲区的办法,可以通过发送额外的空格来解决。

或者通过curl来请求,通过–no-buffer来禁用curl buffer。

curl 'niliu.me' --no_buffer

把上面三个部分串起来,完整的流程如下图【下图只是自己理解使用,仅供参考】:

81df0fac9e3ffa6061207c5d21598fbd.png

第二部分 浏览器实时显示PHP程序输出

一、PHP输出缓冲区和Nginx缓冲区都关闭。

0、php.ini关闭PHP输出缓冲区。

output_buffering = Off

1、关闭nginx缓冲区

方式一、nginx.conf中关闭Nginx fastcgi buffer缓冲区。

fastcgi_buffering off;

方式二、PHP文件中起始行增加头信息。

header(‘X-Accel-Buffering: no’);

2、浏览器请求脚本,先输出hello, 过2秒输出php。

header('X-Accel-Buffering: no');

echo str_pad('hello', 1024);

// 测试发现调用flush()后,chrome不需要向浏览器发送额外的空格,浏览器也可以实时显示

flush();

// 不加flush(), 输出8K内容也是可以的。

// echo str_pad('hello', 1028 * 8);

sleep(2);

echo 'php';

die;

二、PHP输出缓冲区关闭,Nginx缓冲区开启。

0、php.ini关闭PHP输出缓冲区。

output_buffering = Off

1、nginx.conf中开启fastcgi 缓冲,buffer size是默认大小4K。(当然也可以在PHP脚本中通过header(‘X-Accel-Buffering: yes’);开启)

fastcgi_buffering on;

fastcgi_buffer_size 4k;

fastcgi_buffers 8 4k;

2、浏览器请求脚本,先输出hello, 过2秒输出php。

echo str_pad('hello', 1024 * 4);

flush();

sleep(2);

echo 'php';

die;

三、PHP输出缓冲区开启,Nginx缓冲区关闭。

0、php.ini开启PHP输出缓冲区。

output_buffering = 4096

1、nginx.conf中开启fastcgi 缓冲,buffer size是默认大小4K。

fastcgi_buffering off;

2、浏览器请求脚本,先输出hello, 过2秒输出php。

echo str_pad('hello', 1024);

// 要通过ob_flush()把输出缓冲区的内容冲出。

ob_flush();

flush();

sleep(2);

echo 'php';

die;

四、PHP开启多级输出缓冲区,Nginx缓冲区关闭。

0、php.ini开启PHP输出缓冲区。

output_buffering = 4096

1、nginx.conf中开启fastcgi 缓冲,buffer size是默认大小4K。

fastcgi_buffering off;

2、浏览器请求脚本,先输出hello, 过2秒输出php。

// php.ini中开启了缓冲区,在通过ob_start()开启不限制大小的缓冲区,需要通过ob_end_flush()冲出缓冲区内部嵌套的缓冲,在通过ob_flush()冲出到Nginx fastcgi

ob_start();

echo str_pad('hello', 1024);

ob_end_flush();

ob_flush();

flush();

sleep(2);

echo 'php';

die;

注:有些人提到php.ini中开启了PHP输出缓冲区,ob_start()才生效,应该是一个误区,之间没有依赖关系。

五、PHP输出缓冲区开启,Nginx缓冲区开启。

0、php.ini开启PHP输出缓冲区。

output_buffering = 4096

1、nginx.conf中开启fastcgi 缓冲,buffer size是默认大小4K。(当然也可以在PHP脚本中通过header(‘X-Accel-Buffering: yes’);开启)

fastcgi_buffering on;

fastcgi_buffer_size 4k;

fastcgi_buffers 8 4k;

2、浏览器请求脚本,先输出hello, 过2秒输出php。

// nginx fastcgi缓冲区大小是4K,需要发送额外4K空格

echo str_pad('hello', 1024 * 4);

ob_flush();

flush();

sleep(2);

echo 'php';

die;

完整写法,参考php手册flush()。

if (ob_get_level() == 0) {

ob_start();

}

for ($i = 0; $i<10; $i++){

echo "Line to show.";

echo str_pad('',4096)."\n";

ob_flush();

flush();

sleep(2);

}

echo "Done.";

ob_end_flush();

问题:

很多人提到用下面的方式发送额外的空格到浏览器, 但是hello php还是同出显示。

echo str_repeat(' ', 4096); // 注意引号之间有空格

ob_flush();

flush();

sleep(2);

echo 'php';

die;

解决:需要在输出空格之前,输出一个非空字符。比如echo 1。怀疑空格是被Nginx或浏览器过滤了。

第三部分 实际应用

数据下载:参考下篇博文《PHP实现浏览器文件下载》

文件下载:整理待定。

参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值