【问题标题】:Prevent output buffering with PHP and Apache使用 PHP 和 Apache 防止输出缓冲
【发布时间】:2013-02-08 07:56:42
【问题描述】:

我有一个发送大量记录的 PHP 脚本,我想在每条记录可用时立即刷新它:客户端能够在每条记录到达时对其进行处理,它不需要等待整个反应。我意识到整个传输需要更长的时间,因为它需要以多个数据包的形式发送,但它仍然允许客户端更快地开始工作。

我尝试了所有不同的flush()ob_flush() 函数,但似乎没有什么能帮助在页面完成之前通过线路实际发送数据。我已经确认它不是网络浏览器,因为我已经使用 telnet 对其进行了测试。

【问题讨论】:

    标签: php apache output-buffering


    【解决方案1】:

    对我有用的唯一解决方案是将 php.ini 中的 output_buffering 指令设置为“关闭”。我不想为整个服务器执行此操作,只是针对这一特定资源。通常你可以使用 PHP 脚本中的ini_set,但无论出于何种原因,php 都不允许以这种方式设置output_buffering(参见the php manual)。

    事实证明,如果您使用的是 Apache,您可以从您的服务器配置中设置一些 php ini 指令(包括 output_buffering),包括一个 .htaccess 文件。所以我在 .htaccess 文件中使用了以下内容来禁用该文件的 output_buffering:

    <Files "q.php">
        php_value output_buffering Off
    </Files>
    

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

    【讨论】:

    • 不确定这是否总是错误的或 PHP5.6 中发生了某些变化,但我必须使用 php_flag output_buffering Off 才能让 Off 工作。
    • 我将 php.ini 中的“output_buffering”从 4096 设置为 Off,然后重新启动整个服务器,使用 phpinfo() 进行验证;但我仍然面临同样的问题,服务器在向浏览器发送数据之前正在等待页面加载。 Apache2 或 Ubuntu 16.04 中是否有其他设置?我正在使用 PHP7。
    【解决方案2】:

    你没有提到你正在使用什么网络服务器,但我会在这里冒险并猜测 Apache2。我击中了您描述的几乎相同的东西。我试图让我的 cgi 脚本在它准备好时传回信息,而不是缓冲整个事情。在 curl 等中快速工作,但在浏览器(几乎所有浏览器)中缓冲,这至少令人抓狂。我经历了你描述的确切步骤。我的解决方案是修改 Apache2 中的 sites-enabled/terrifico.com 配置文件(有问题的行以

    SetEnvIfNoCase

    (您可以忽略该行上方和下方的内容,我只是将其显示以供参考。)

    <VirtualHost *:80>
    ServerAdmin webmaster@localhost
    ServerName test.terrifico.com
    ServerAlias test.terrifico.com
    
    SetEnvIfNoCase Request_URI \.cgi$ no-gzip dont-vary
    
    DocumentRoot /var/www/test.terrifico.com
    

    从盯着来回的网络流量看,我终于明白浏览器在宣传它接受任何东西(即文本)的通货紧缩。例如,这就是浏览器和 curl 之间的区别。重点是

    接受编码:gzip、deflate、sdch

    有一点关于chunking,但这并没有影响这个特定的问题。因此,浏览器请求 mod_deflate 启动,这打败了我在 cgi 脚本中小心地吐出字节。您可以在浏览器中更改它,但在服务器上更改一次似乎更明智。

    也许这会有所帮助。

    【讨论】:

      【解决方案3】:

      要在 PHP 运行时关闭输出缓冲而不更改 php.ini 或拥有 .htaccess 文件,只需在脚本开头使用 ob_end_flush()ob_end_clean()。例如:

      这应该在没有缓冲的情况下输出:

      <?php
      ob_end_clean();
      
      for ($i = 0; $i < 5; $i++)
      {
          echo "$i\n";
          flush();
          usleep(0.5e6);
      }
      

      如果output_buffering 开启,则无论flush() 调用如何,此输出都带有缓冲(一次全部):

      <?php
      
      for ($i = 0; $i < 5; $i++)
      {
          echo "$i\n";
          flush();
          usleep(0.5e6);
      }
      

      尽管有它的名字,ob_implicit_flush 调用 flush(),而不是 ob_flush(),在每个输出之后隐式调用。在开始关闭输出缓冲区后,这在这种情况下会很方便:

      <?php
      ob_end_clean(); // disable output buffer
      ob_implicit_flush(); // call flush() automatically after every output
      
      for ($i = 0; $i < 5; $i++)
      {
          echo "$i\n";
          usleep(0.5e6);
      }
      

      这修复了 PHP 方面。 mod_deflate 或类似的东西可能会发生其他事情(请参阅 Ted Collins 的回答),我观察到 Firefox 至少需要 1024 字节才能开始输出任何内容。

      【讨论】:

      • 最佳答案,ob_implicit_flush() 是缺少的
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-10-19
      • 2016-02-11
      • 2011-03-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多