15

長時間実行されるため、実行中に進行状況をエコーするスクリプトがいくつかあります。基本的に、処理されたデータのループされた各行の最後で、それぞれが次のことを行います。

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

これは何年もうまく機能していましたが、その後、いくつかのサーバーで PHP 5.3.x と Apache 2.2.x にアップグレードしました。ここで、バッファーに空白を埋め込んだり、「ob_implicit_flush(1)」を設定したりしても、コマンドで出力を表示できません。

1 つのサーバーで引き続き出力が表示されますが、チャンクになっています。5 分近くかかる場合があり、その後突然、一連のドットが画面に表示されます。他のサーバーでは、スクリプトの実行が完全に終了するまで何も得られません。

php.ini ファイルと httpd.conf ファイルを調べて、異なるサーバー間で何が変更されたのかを調べてみましたが、明らかに何かが欠けています。

また、影響を受けるスクリプトの .htaccess で mod_deflate を無効にしようとしましたが、それも役に立ちません (mod_gzip を無効にすると、問題はすぐに修正されます)。

誰かがこれで正しい方向に私を向けることができますか? スクリプトの実行をリアルタイムで監視できないことは、あらゆる種類の問題を引き起こしていますが、これらの古い PHP バージョンにこれ以上とどまることはできません。

さらに奇妙なことに、サーバーを PHP 5.2.17 にダウングレードしようとしましたが、ダウングレード後も出力バッファーの問題が残りました。これは、Apache 2 がそのまま残されているため、Apache が PHP 出力を処理する方法に関連していると思われます。

4

5 に答える 5

13

ob_flush()(flush())はPHPバッファーのみをフラッシュします-Webサーバーはバッファー自体を維持します。奇妙に聞こえるかもしれませんが、バッファを早期にフラッシュすると、実際にはサーバーのスループットが低下するため、最近のバージョンのapacheバッファはより積極的になります。HTTPチャンクエンコーディングを使用する場合の圧縮と部分レンダリングに関連する恐ろしい問題もあります。

ページにコンテンツを段階的に追加する場合は、ajaxまたはwebsocketを使用して一度に少しずつ追加します。

于 2012-12-06T22:56:09.190 に答える
6

この問題は、php のバージョンではなく、サーバー (apache) に関係しています。

1 つのオプションは出力バッファリングを無効にすることですが、サイトの他の部分でパフォーマンスが低下する可能性があります

アパッチ上

output_buffering=off.htaccess ファイルを含むサーバー構成からphp ini ディレクティブ ( ) を設定します。そのため、ファイルで次を使用して、.htaccessその1つのファイルだけでoutput_bufferingを無効にしました。

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

そして、私の静的サーバー構成では、それをファイルで許可するために必要なAllowOverride Options=php_valueもの (または のような大きなハンマー) が必要でした。AllowOverride All.htaccess

Nginxで

Nginx のバッファリングを無効にするには (構成ファイルに「proxy_buffering off;」を追加し、Nginx を再起動します)

于 2015-08-22T11:33:51.377 に答える
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. デフレのためにバッファリングする mod を無効にする - mod_deflate と mod_gzip を無効にする

  4. PHP と Apache の間で char エンコーディングが一致していることを確認してください - default_charset = "utf-8";php.ini とAddDefaultCharset utf-8httpd.conf に追加してください)。

  5. FastCgi または Fcgid でのバッファリングの無効化 - -flush オプションを追加することで、FastCgi でのバッファリングを無効にできます。詳細は上記リンクにて。上にリストされている Fcgid のオプション。

私の知る限り、これらはサーバー上の唯一のバッファーです。明らかに、サーバーとブラウザの間の他のデバイスもバッファリングする可能性があります。たとえば、プロキシは、完全な出力が提供されるのを待ってから渡します。Fiddler ( https://www.telerik.com/fiddler ) はこれを行います。

于 2015-08-22T13:22:58.543 に答える
4

既存のスクリプトを編集したり、サーバー構成を変更して出力のバッファリングを停止したりする必要がない、この問題の回避策が考えられます。ラッパー スクリプトを使用すると、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>

スクリプトに Web リクエストを送信するとproc_watcher.php?action=start、長時間実行されるプロセスがバックグラウンドで開始され、出力ファイルの内容が毎秒 Web ブラウザに返されます。

ここでの秘訣はnohup php ./long_process.php > ./output.txt &、プロセスをバックグラウンドで実行し、出力を STDOUT ではなくファイルに送信するコマンド ラインです。

于 2015-08-22T20:17:07.123 に答える
3

I tried everything to get this to work, including all known settings listed above. I've been trying to use PHP to serve chunked video files using HTTP_RANGE and it wasn't working.

After pulling most of my hair out, i found the answer: you have to output at least one byte more than the buffer size in order for it to output to the browser. Here's the script that ended up working for me:

<?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();
}

Hope this helps someone!

于 2017-07-18T08:52:43.877 に答える