![](/img/trans.png)
[英]Why do I need to use many flushing functions to flush the output buffer (using wamp, 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版本有關。
一種選擇是禁用輸出緩沖,但性能可能會在站點的其他部分受到影響
從服務器配置中設置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的緩沖(將“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也是緩沖區(如原始問題中所述)
所以要檢查的步驟是:
刷新PHP緩沖區 - (如問題中所述)
關閉Apache緩沖 - 在.htaccess中添加php_value output_buffering off
禁用用於緩解通貨緊縮的mods - 禁用mod_deflate和mod_gzip
確保您的char編碼在PHP和Apache之間匹配 - 添加default_charset = "utf-8";
在php.ini和httpd.conf中的AddDefaultCharset utf-8
中)
在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.