PHP Flush, который работает даже в Nginx

Можно ли повторять каждый раз, когда цикл выполняется? Например:

foreach(range(1,9) as $n){
    echo $n."\n";
    sleep(1);
}

Вместо того, чтобы печатать все, когда цикл завершен, я бы хотел, чтобы он печатал каждый результат за раз.


person Roger    schedule 02.02.2011    source источник


Ответы (8)


Самый простой способ устранить буферизацию nginx — создать заголовок:

header('X-Accel-Buffering: no');

Это устраняет как proxy_buffering, так и (если у вас nginx >= 1.5.6), fastcgi_buffering. Бит fastcgi имеет решающее значение, если вы используете php-fpm. Заголовок также гораздо удобнее делать по мере необходимости.

Документы по X-Accel-Buffering Документы по fastcgi_buffering

person Andy Fowler    schedule 19.04.2014
comment
Отлично, это сработало отлично для меня. Использование nginx 1.6.2 в Ubuntu 14.04. Спасибо за этот комментарий. - person Bunkai.Satori; 21.02.2015
comment
Большое спасибо! Жаль, что я не обнаружил это час назад. Простое, элегантное однострочное решение :) - person digitaltoast; 12.02.2016
comment
Это то, что сделало это для меня. - person logic; 15.12.2016
comment
Мне нравится это простое решение в скрипте, которое переопределяет файлы conf. Я заставил это работать, используя set_time_limit(0), отправляя только этот заголовок, без заголовка типа контента, за которым следует мой эхо-код HTML (или require_once()), затем ob_flush(), а затем flush(). Затем я мог бы выполнить свою длинную задачу (задачу электронной коммерции), а затем вывести остальную часть HTML, когда закончу, чтобы закрыть страницу, которая добавляет некоторый Javascript, который немедленно перенаправляет страницу на получение их заказа. - person Volomike; 24.04.2017
comment
Это сработало для меня, но сломало тесты webpagetest.org. Что здесь может пойти не так? - person Aftab Naveed; 06.11.2017
comment
в отличие от принятого выше решения, которое не сработало, это сработало. Спасибо! - person ExternalUse; 07.02.2018

ОКОНЧАТЕЛЬНОЕ РЕШЕНИЕ

Итак, что я узнал:

Flush не будет работать под mod_gzip Apache или gzip Nginx, потому что, по логике, он сжимает содержимое, а для этого он должен буферизовать содержимое, чтобы сжать его. Любое сжатие веб-сервера повлияет на это. Короче говоря, на стороне сервера нам нужно отключить gzip и уменьшить размер буфера fastcgi. Так:

  • В php.ini:

    . output_buffering = Выкл.

    . zlib.output_compression = Выкл.

  • В nginx.conf:

    . сжатие выключено;

    . прокси_буферизация выключена;

Также имейте под рукой эти строки, особенно если у вас нет доступа к php.ini:

  • @ini_set('zlib.output_compression',0);

  • @ini_set('implicit_flush',1);

  • @ob_end_clean();

  • set_time_limit (0);

Наконец, если он у вас есть, прокомментируйте код ниже:

  • ob_start('ob_gzhandler');

  • ob_flush();

Тестовый код PHP:

ob_implicit_flush(1);

for($i=0; $i<10; $i++){
    echo $i;

    //this is for the buffer achieve the minimum size in order to flush data
    echo str_repeat(' ',1024*64);

    sleep(1);
}

Связанный:

person Roger    schedule 16.02.2011
comment
Потрясающий. Спасибо. И если у кого-то есть проблемы с этим, попробуйте перезапустить свой сервер... (Эй, это то, что сработало для меня. Не знаю, почему.) - person Matt; 04.09.2012
comment
@Matt php и настройки сервера не применяются, пока вы не перезапустите - person Timo Huovinen; 27.09.2013
comment
@TimoHuovinen Однако в моем случае я не менял php.ini. - person Matt; 27.09.2013
comment
@matt хорошо, тогда это странно :) - person Timo Huovinen; 28.09.2013
comment
service nginx restart перезапускается с новым nginx.conf, по крайней мере, в CentOS ;-) - person PJ Brunet; 12.11.2013
comment
str_repeat() должна быть равна значению output_buffering. Если это 4096 => str_repeat(' ', 4096)."\n";, чтобы убедиться, что контент отправлен. - person Yvan; 17.11.2014
comment
Я получил несколько лучших результатов, используя комбинацию приведенных выше команд, но я должен спросить, есть ли какие-либо команды/конфигурации, которые я должен использовать с apache (поскольку я не использую nginx). - person The Unknown Dev; 14.01.2015

Простое решение на сервере nginx:

fastcgi_keep_conn on; # < solution

proxy_buffering off;
gzip off;
person Ondrej Prochazka    schedule 30.11.2012
comment
Я бы дал +2, если бы мог. После того, как вы попробовали все другие решения, это то, что сработало. - person jrhorn424; 07.12.2013
comment
То же самое, лучший ответ здесь. - person galex; 19.08.2014
comment
Отличный метод. Также обратите внимание, что все три директивы можно использовать в http, server или location контекстах. Таким образом, вы можете быть очень избирательны, где это применимо. - person Majid Fouladpour; 17.09.2019

Я не хотел отключать gzip для всего сервера или всего каталога, только для нескольких скриптов, в нескольких конкретных случаях.

Все, что вам нужно, это это, прежде чем что-либо будет эхом:

header('Content-Encoding: none;');

Затем выполните промывку как обычно:

ob_end_flush();
flush();

Nginx, кажется, улавливает, что кодировка отключена, и не сжимает.

person Redzarf    schedule 27.06.2013
comment
+1 за решение только для PHP (которое можно легко включить ad hoc). Обратите внимание, что для того, чтобы это работало, также требовалось header('X-Accel-Buffering: no');. - person kbtz; 24.08.2015
comment
Это header('Content-Encoding: none;'); сработало для меня. Нет необходимости в ob_end_flush() или flush(), просто убедитесь, что вывод достаточно большой, используя echo str_repeat(' ', 1024*64); - person ; 14.02.2017

Вам нужно сбросить буфер php в браузер

foreach(range(1,9) as $n){
    echo $n."\n";
    flush();
    sleep(1);
}

См.: http://php.net/manual/en/function.flush.php

person Petah    schedule 02.02.2011
comment
Вы также можете вызвать @ob_flush(). - person RC.; 02.02.2011
comment
@RC: просто любопытно, зачем тебе '@' перед ob_flush? Я бы использовал: if(ob_get_level() > 0) ob_flush(); - person meze; 02.02.2011
comment
@Roger, вы используете буферизацию вывода или включили сжатие вывода? - person Petah; 02.02.2011
comment
@meze, он подавляет ошибку, которую вы получите, если буферизация вывода не включена. - person Petah; 02.02.2011
comment
@Petah: тогда ob_get_level вернет 0, нет необходимости делать @-хаки. - person meze; 02.02.2011
comment
@meze, личные предпочтения. Я бы в таком случае отключил всю буферизацию вывода while (ob_get_level() > 0) ob_end_clean() - person Petah; 02.02.2011
comment
@ Роджер, похоже, у тебя проблемы с буферизацией вывода. Вы на виртуальном хостинге? У вас есть демонстрационная страница, на которую я могу посмотреть, и/или вывод phpinfo. - person Petah; 03.02.2011

Я обнаружил, что вы можете установить:

header("Content-Encoding:identity");

в вашем php-скрипте, чтобы отключить сжатие nginx без необходимости изменять nginx.conf

person ttk    schedule 06.03.2014

Этого можно добиться, очистив выходной буфер в середине цикла.

Пример:

ob_start();
foreach(range(1,9) as $n){
    echo $n."\n";
    ob_flush();
    flush();
    sleep(1);
}

Обратите внимание, что ваши настройки php.ini могут повлиять на то, будет ли это работать или нет, если у вас включено сжатие zlib.

person Andy Baird    schedule 02.02.2011
comment
Здесь не работает. Он печатает все в конце выполнения цикла. - person Roger; 02.02.2011
comment
Проверьте свой php.ini на следующее: output_buffering = Off zlib.output_compression = Off (убедитесь, что они оба установлены таким образом) - person Andy Baird; 02.02.2011

У меня возникла проблема с gzip из-за моего движка php-fpm. этот код единственный, который работает для меня:

function myEchoFlush_init() {
    ini_set('zlib.output_compression', 0);
    ini_set('output_buffering', 'Off');
    ini_set('output_handler', '');
    ini_set('implicit_flush', 1);
    ob_implicit_flush(1);
    ob_end_clean();
    header('Content-Encoding: none;');

}

function myEchoFlush($str) {
    echo $str . str_repeat(' ', ini_get('output_buffering') * 4) . "<br>\n";
}

Это моя тестовая функция: она проверяет max_execution_time:

public function timeOut($time = 1, $max = 0) {
    myEchoFlush_init();
    if ($max) ini_set('max_execution_time', $max);
    myEchoFlush("Starting infinite loop for $time seconds. It shouldn't exceed : " . (ini_get('max_execution_time')));
    $start = microtime(true);
    $lastTick = 1;
    while (true) {
        $tick = ceil(microtime(true) - $start);
        if ($tick > $lastTick) {
            myEchoFlush(microtime(true) - $start);
            $lastTick = $tick;
        }
        if ($tick > $time) break;
    }
    echo "OK";
}
person Nicolas Thery    schedule 04.05.2017
comment
Не используйте сон, так как он может не учитываться во времени выполнения. - person Nicolas Thery; 04.05.2017
comment
myEchoFlush_init() работал, за исключением того, что мне пришлось удалить из него заголовок. - person Dellowar; 02.05.2019