PHP实时输出需逐层关闭缓冲:禁用Nginx gzip/fastcgi_buffering、Apache mod_deflate、PHP output_buffering/zlib压缩,首行输出1024空格绕过Chrome缓冲,配合ob_flush()和flush(),并设置SSE响应头及心跳保活。

PHP 实时输出接口为什么用 flush() 没反应?
多数人卡在这一步:写了 echo、ob_flush()、flush(),但浏览器还是等全部执行完才显示。根本原因不是 PHP 代码错了,而是中间环节在“拦路”。
- Web 服务器(如 Nginx)默认开启
gzip或缓冲响应体,会攒够一定字节才发;需关闭:gzip off;(Nginx)或设置fastcgi_buffering off; - Apache 的
mod_deflate同样会压缩并缓存输出,需禁用或配置SetEnv no-gzip 1 - PHP 自身的输出缓冲链有好几层:
output_buffering(php.ini)、zlib.output_compression必须关掉,建议在脚本开头加:ini_set('output_buffering', 'Off'); ini_set('zlib.output_compression', false); - 浏览器也可能缓冲——特别是 Chrome 对小响应体有最小字节数要求(约 1KB),可在首行输出 1024 字符空格(
str_repeat(' ', 1024))绕过
用 while(true) 长轮询还是 EventSource?
别硬写长轮询。PHP 天然不适合维持大量并发长连接,while(true) + sleep() 在 Apache/PHP-FPM 下极易耗尽 worker 进程,且无法优雅中断。
-
EventSource(SSE)是更轻量的选择:前端用new EventSource('/stream.php'),后端保持连接、按格式输出data: ...\n\n,PHP 只需每轮echo "data: ".json_encode($msg)."\n\n"; ob_flush(); flush(); - 必须设置响应头:
header('Content-Type: text/event-stream'); header('Cache-Control: no-cache'); header('X-Accel-Buffering: no');(最后一项专治 Nginx 缓冲) - 若需双向实时(如聊天),SSE 不行,得换 WebSocket(用 Swoole 或 Workerman 实现),PHP 原生不支持服务端 WebSocket
ob_end_flush() 和 ob_flush() 到底该用哪个?
混淆点在于 PHP 输出缓冲层级。简单说:ob_flush() 清当前一级输出缓冲区,flush() 把 Web 服务器缓存里的内容真正推给客户端;ob_end_flush() 是清完还销毁缓冲区——一旦调了,后续 echo 就直通底层,不能再用 ob_*() 控制。
- 推荐固定组合:
ob_flush(); flush();(反复用) - 如果开头用了
ob_start(),结尾想彻底释放,才用ob_end_flush();但实时流中一般不需要销毁缓冲区 - 注意顺序:必须先
ob_flush()(把 PHP 用户缓冲刷到 SAPI 层),再flush()(把 SAPI 缓冲推给客户端),反了无效
如何避免超时和连接被意外断开?
PHP 默认 max_execution_time=30,Nginx 默认 proxy_read_timeout=60,这些都会杀死长连接。不调它们,实时流活不过一分钟。
立即学习“PHP免费学习笔记(深入)”;
- PHP 端:脚本开头加
set_time_limit(0);,并确保ignore_user_abort(true)(防止用户关页面导致脚本终止) - Nginx 端:在 location 块里配
proxy_read_timeout 3600;、proxy_buffering off;、chunked_transfer_encoding off; - 务必加心跳保活:每 15–30 秒输出一次注释行(
echo ": keep-alive\n\n"; ob_flush(); flush();),避免代理或防火墙因空闲断连
fastcgi_buffering 和浏览器的 1KB 缓冲门槛。











