Предотвращение буферизации вывода с помощью PHP и Apache
У меня есть PHP-скрипт, который отправляет большое количество записей, и я хочу удалить каждую запись, как только она станет доступной: клиент может обрабатывать каждую запись по мере ее поступления, ему не нужно ждать полного ответа. Я понимаю, что для всей передачи требуется немного больше времени, потому что ее нужно отправлять несколькими пакетами, но это все равно позволяет клиенту начать работать раньше.
Я перепробовал все различные функции flush()
и ob_flush()
, но, похоже, ничего не помогает получите данные, фактически отправленные по строке, до того, как страница будет завершена. Я подтвердил, что это не веб-браузер, потому что я протестировал его с помощью telnet.
3 answers
Единственным решением, которое сработало для меня, было установить директиву output_buffering
в php.ini на "Выкл". Я не хотел делать это для всего сервера, только для одного конкретного ресурса. Обычно вы можете использовать ini_set
из PHP-скрипта, но по какой-то причине php не позволяет устанавливать output_buffering
таким образом (см. руководство по php).
Оказывается, что если вы используете Apache, вы можете установить некоторые директивы php ini (включая output_buffering
) из конфигурации вашего сервера, включая .htaccess файл. Поэтому я использовал следующее в файле .htaccess, чтобы отключить буферизацию вывода только для этого одного файла:
<Files "q.php">
php_value output_buffering Off
</Files>
А затем в моей конфигурации статического сервера мне просто понадобился AllowOverride Options=php_value
(или больший молоток, напримерAllowOverride All
), чтобы это было разрешено в файле.htaccess.
Вы не упомянули, какой веб-сервер вы используете, но я собираюсь рискнуть и угадать Apache2. Я попал почти в то же самое, что вы описываете. Я пытался заставить свой cgi-скрипт передавать информацию в том виде, в каком она была готова, вместо того, чтобы буферизировать все это. Работал быстро в curl и т. Д., Но буферизовался в браузере (практически в любом браузере), что, по крайней мере, сводило с ума. Я прошел в точности те шаги, которые вы описали. Решение в моем случае состояло в том, чтобы изменить sites-enabled/terrifico.com
файл конфигурации в Apache2 (рассматриваемая строка начинается с
Сетенвифноказ
(Вы можете игнорировать материал выше и ниже этой строки, я просто показываю его для справки о том, где я его разместил.)
<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, выкачать, sdch
Там было немного о chunking
, но это не повлияло на эту конкретную проблему. Итак, браузер запрашивал mod_deflate
для запуска, что помешало моему тщательному изверганию байтов, когда я получил их в своем cgi-скрипте. Вы могли бы изменить его в браузере, но мне показалось более разумным изменить его на сервере один раз для работы.
Возможно, это поможет.
Чтобы отключить буферизацию вывода во время выполнения в 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
или чем-то подобным происходит что-то еще (см. Ответ Теда Коллинза), и я заметил, что Firefox требуется не менее 1024 байт, прежде чем он начнет выводить что-либо вообще.