簡體   English   中英

PHP輸出緩沖區沒有刷新

[英]PHP output buffer not flushing

我有許多腳本在執行時回顯進度,因為它們長時間運行。 每個處理的每個循環數據行的末尾基本上都執行以下操作:

echo '.';
@ob_flush();
flush();

這工作好多年,然后我在幾台服務器上升級到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已經存在。

ob_flush()(flush())只刷新PHP緩沖區 - 網絡服務器本身維護一個緩沖區。 雖然聽起來很奇怪,但提前刷新緩沖區實際上會降低服務器的吞吐量,因此最近版本的apache會更加積極地緩沖。 使用HTTP分塊編碼時,還存在與壓縮和部分渲染相關的可怕問題。

如果要逐步向頁面添加內容,請使用ajax或websockets一次添加一些內容。

這個問題更多地與您的服務器(apache)而不是php版本有關。

一種選擇是禁用輸出緩沖,但性能可能會在站點的其他部分受到影響

在Apache上

從服務器配置中設置php ini指令( output_buffering=off ),包括.htaccess文件。 所以我在.htaccess文件中使用以下內容來禁用僅為該文件的output_buffering:

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

然后在我的靜態服務器配置中,我只需要AllowOverride Options=php_value (或更大的錘子,如AllowOverride All ),以便在.htaccess文件中允許。

在Nginx上

禁用Nginx的緩沖(將“proxy_buffering off;”添加到配置文件並重新啟動Nginx

最初可能是原始問題中描述的變化是新設置使用的是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緩沖 - 在.htaccess中添加php_value output_buffering off

  3. 禁用用於緩解通貨緊縮的mods - 禁用mod_deflate和mod_gzip

  4. 確保您的char編碼在PHP和Apache之間匹配 - 添加default_charset = "utf-8"; 在php.ini和httpd.conf中的AddDefaultCharset utf-8中)

  5. 在FastCgi或Fcgid中禁用緩沖 - 您可以通過添加-flush選項在FastCgi中禁用緩沖。 上面鏈接中的詳細信息。 上面列出了Fcgid的選項。

據我所知,這些是服務器上唯一的緩沖區; 顯然,服務器和瀏覽器之間的其他設備也可以緩沖,例如代理可以在傳遞之前等待提供完整輸出。 Fiddler( https://www.telerik.com/fiddler )這樣做,它經常讓我知道,直到我記得。

有可能解決此問題,不需要您編輯現有腳本或修改服務器配置以停止緩沖輸出。 通過使用包裝器腳本,您可以在后台從提供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";
?>

腳本初始化長時間運行的進程並監視輸出

<?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>

如果您向proc_watcher.php?action=start發送Web請求proc_watcher.php?action=start腳本應該在后台啟動長時間運行的進程,然后每秒將輸出文件的內容返回到Web瀏覽器。

這里的技巧是命令行nohup php ./long_process.php > ./output.txt & ,它在后台運行進程並將輸出發送到文件而不是STDOUT。

我嘗試了一切來實現這一點,包括上面列出的所有已知設置。 我一直在嘗試使用PHP來使用HTTP_RANGE來提供分塊視頻文件,但它無法正常工作。

把大部分頭發拉出后,我找到了答案:你必須輸出比緩沖區大小多一個字節才能輸出到瀏覽器。 這是最終為我工作的腳本:

<?php 
// Close open sessions
session_write_close();

// Turn off apache-level compression
@apache_setenv('no-gzip', 1);

// Turn off compression
@ini_set('zlib.output_compression', 0);

// Turn error reporting off
@ini_set('error_reporting', E_ALL & ~ E_NOTICE);

// Tell browser not to cache this
header("Cache-Control: no-cache, must-revalidate");

// close any existing buffers
while (ob_get_level()) ob_end_clean();

// Set this to whatever you like
$buffer = 8096;

for($i = 1; $i <= 8; $i++) 
{
    // Start a output buffer with specified size
    ob_start(null,$buffer,PHP_OUTPUT_HANDLER_FLUSHABLE);
    // Output exactly one byte more than that size 
    // \n == 2 bytes, so 8096-1+2 = 8097
    echo str_repeat('=', $buffer-1)."\n";
    // 0.25s nap
    usleep(250000);
    // End output buffering and flush it
    ob_end_flush();
    flush();
}

希望這有助於某人!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM