PHP输出缓冲区没有刷新

eCo*_*Evo 14 php apache

我有许多脚本在执行时回显进度,因为它们长时间运行.每个处理的每个循环数据行的末尾基本上都执行以下操作:

echo '.';
@ob_flush();
flush();
Run Code Online (Sandbox Code Playgroud)

这工作好多年,然后我在几台服务器上升级到PHP 5.3.x和Apache 2.2.x. 现在,即使我用空格填充缓冲区或设置"ob_implicit_flush(1)",我也无法通过命令显示输出.

一台服务器仍然显示输出,但它是块.它可能需要将近5分钟,然后屏幕上突然出现一串点.使用其他服务器,在脚本完成执行之前我什么也得不到.

我已经尝试查看php.ini和httpd.conf文件,看看我是否能弄清楚不同服务器之间的变化,但我显然遗漏了一些东西.

我也尝试在受影响的脚本中禁用.htaccess中的mod_deflate,但这也无济于事(禁用用于立即解决问题的mod_gzip).

请有人指出我正确的方向吗?无法实时监控脚本执行会导致各种问题,但我们不能再继续使用这些旧的PHP版本了.

在一个更奇怪的方面,我确实尝试将服务器降级到PHP 5.2.17,但在降级后输出缓冲区问题仍然存在.这让我怀疑它与Apache处理PHP输出的方式有关,因为Apache 2已经存在.

sym*_*ean 13

ob_flush()(flush())只刷新PHP缓冲区 - 网络服务器本身维护一个缓冲区.虽然听起来很奇怪,但提前刷新缓冲区实际上会降低服务器的吞吐量,因此最近版本的apache会更加积极地缓冲.使用HTTP分块编码时,还存在与压缩和部分渲染相关的可怕问题.

如果要逐步向页面添加内容,请使用ajax或websockets一次添加一些内容.


Uri*_*ren 5

这个问题更多地与您的服务器(apache)而不是php版本有关.

一种选择是禁用输出缓冲,但性能可能会在站点的其他部分受到影响

在Apache上

output_buffering=off从服务器配置中设置php ini指令(),包括.htaccess文件.所以我在.htaccess文件中使用以下内容来禁用仅为该文件的output_buffering:

<Files "q.php">
    php_value output_buffering Off
</Files>
Run Code Online (Sandbox Code Playgroud)

然后在我的静态服务器配置中,我只需要AllowOverride Options=php_value(或更大的锤子AllowOverride All),以便在.htaccess文件中允许它.

在Nginx上

禁用Nginx的缓冲(将"proxy_buffering off;"添加到配置文件并重新启动Nginx


Rob*_*bie 5

最初可能是原始问题中描述的变化是新设置使用的是FastCgi(http://www.fastcgi.com/mod_fastcgi/docs/mod_fastcgi.html),并且启用缓冲是默认设置.

但还有其他因素需要检查:

如果你正在使用Fcgid,那么这也有缓冲:http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html#fcgidoutputbuffersize

如果PHP和Apache之间的字符集不匹配 - 这可以解决问题.

mod_deflate和mod_gzip也是缓冲区(如原始问题中所述)

所以要检查的步骤是:

  1. 刷新PHP缓冲区 - (如问题中所述)

  2. 关闭Apache缓冲 - 添加php_value output_buffering off.htaccess

  3. 禁用用于缓解通货紧缩的mods - 禁用mod_deflate和mod_gzip

  4. 确保您的char编码在PHP和Apache之间匹配 - default_charset = "utf-8";在php.ini和AddDefaultCharset utf-8httpd.conf中添加)

  5. 在FastCgi或Fcgid中禁用缓冲 - 您可以通过添加-flush选项在FastCgi中禁用缓冲.上面链接中的详细信息.上面列出了Fcgid的选项.

据我所知,这些是服务器上唯一的缓冲区; 显然,服务器和浏览器之间的其他设备也可以缓冲,例如代理可以在传递之前等待提供完整输出.Fiddler(https://www.telerik.com/fiddler)这样做,它经常让我知道,直到我记得.


Gle*_*len 5

有可能解决此问题,不需要您编辑现有脚本或修改服务器配置以停止缓冲输出.通过使用包装器脚本,您可以在后台从提供Web请求的php脚本开始长时间运行的进程.然后,您可以将长时间运行的进程的输出通过管道传输到文本文件中,该文件可以通过轮询轻松读取以查找脚本的当前进度.示例如下:

漫长的进程脚本

<?php
// long_process.php
echo "I am a long running process ";
for ($i = 0; $i < 10; $i++) {
    echo ".";
    sleep(1);
}
echo " Processing complete";
?>
Run Code Online (Sandbox Code Playgroud)

脚本初始化长时间运行的进程并监视输出

<?php
    // proc_watcher.php
    $output = './output.txt';
    if ($_GET['action'] == 'start') {
        echo 'starting running long process<br>';
        $handle = popen("nohup php ./long_process.php > $output &", 'r');
        pclose($handle);
    } else {
        echo 'Progress at ' . date('H:i:s') . '<br>';
        echo file_get_contents($output);
    }
    $url = 'proc_watcher.php';
?>
<script>
    window.setTimeout(function() {
         window.location = '<?php echo $url;?>';
    }, 1000);
</script>
Run Code Online (Sandbox Code Playgroud)

如果向proc_watcher.php?action=start脚本发送Web请求,则应在后台启动长时间运行的进程,然后每秒将输出文件的内容返回到Web浏览器.

这里的技巧是命令行nohup php ./long_process.php > ./output.txt &,它在后台运行进程并将输出发送到文件而不是STDOUT.