Main > Apache | nginx | Servers > Буферизация вывода и flush

Буферизация вывода и flush

13.02.2012 10 comments » Views: 10,572

PHP

Столкнулся с проблемой, которая не дает мне покоя, уже не первый раз. Но вот пришло время её решить.

Не работает flush(); на хостинге заказчика.

Тестовый пример:

Есть доступ к конфигурации php.ini

PHP работает как модуль Апача.

Работающие модули ISP Manager

PHP Info:

Configure command:

Core:

Zlib:

Пошел я в гугл, долго искал, читал и пробовал.. Ничего не помогает.. Я грешил на какую-то буферизирующую софтинку, и решил в очередной раз проверить, не загоняет ли сервак свои ответы в gzip, т.к. это обозначало бы что они кешируются.. И вот что я увидел

Кеширования нет, но .. Server: nginx
Вот оно что, в качестве фронтенда, стоял nginx, и я заподозрил именно его в таком поведении.

Нашел конфиг, он находился в /etc/nginx/nginx.conf, посмотрел там, и нашел вот это:

С конфигурацией nginx я столкнулся впервые, знал лишь то, что разработал его Игорь Сысоев и он русский, а это значит, что должно быть достаточно мануалов. Пошел в поиск и попал к нему на сайт, там меня интересовал раздел документации, а именно модуль ngx_http_proxy_module. Вот там я и нашел ответ на свой вопрос, а именно директиву proxy_buffering:

Директива разрешает использовать буферизацию ответа проксируемого сервера. Если буферизация включена, то nginx принимает ответ проксируемого сервера как можно быстрее, сохраняя его в буфера, заданные директивами proxy_buffer_size и proxy_buffers. Если ответ не помещается полностью в память, то его часть записывается на диск.

Если буферизация выключена, то ответ синхронно передаётся клиенту сразу же по мере его поступления. nginx не пытается считать весь ответ проксируемого сервера, максимальный размер данных, который nginx может принять от сервера задаётся директивой proxy_buffer_size.

Пошел искать эту директиву в своем конфиге, но её там небыло. Ну, раз нет, тогда добавим и я её прописал в секции Main location:

(!) Не забудьте про точку запятую в конце. Я забыл, и в начале подумал, что это лекарство мне не помогло.

Конфигурация, сохранена. Остается вопрос, как же рестартануть nginx с правильной конфигурацией. Это делается таким образом (в консоли):
1) #ps ax | grep nginx
2) ищем в выданном нам списке nginx: master process
3) запоминаем его pid (это число в начале строки), пусть это будет 7889
4) #top
5) теперь нажимаем k
6) На вопрос PID to kill отвечаем, номером нашего процесса, т.е. 7889, нажимаем Enter
7) На вопрос Kill PID 7889 with signal [15]: отвечаем HUP, нажимаем Enter (подробнее про сигналы тут)
8) нажимаем Ctrl+C для выхода из top'a
9) Можно тестировать наш скрипт с flush

У меня все заработало и я был счастлив! Как всегда, проблема которую не можешь решить несколько дней, решается всего одной маленькой строчкой 🙂

Author: | Rating: 4/5 | Tags:

10 comments.

Write a comment
  1. infernix Reply
    02.12.2015 в 8:38 am
    А в .htaccess добавить строку:
    php_flag output_buffering off
    разве не решает проблему?
    • Vitaliy Orlov Reply
      02.12.2015 в 9:53 am
      В некоторых случаях это решит проблему, но конкретно в описываемом, думаю, что нет. Т.к. кеширование происходило на стороне nginx-а, а не php.
  2. Mihalch Reply
    02.09.2014 в 8:46 pm
    Да, на Денвере работает. А, например, в windows server 2012 стоит IIS8 и размер буфера по умолчанию 4МБ. Его, конечно, можно заполнить подобным образом через echo str_pad(' ', 4294967296); , но в этом случае вывод текста происходит только первый раз, а всё остальное только в конце скрипта. Причём даже если поставить str_pad в цикл. Да и гонять постоянно 4МБ тоже нагрузка.
    В настройках IIS8 буферизация вывода так запросто не отключается. Буферизацию в IIS8 можно выключить или уменьшить, если поправить файл
    C:\Windows\System32\inetsrv\config\schema\IIS_schema.xls.
    Но у админа нет прав на его запись, и даже при остановленном IIS система показывает access denied. И в свойствах файла права не устанавливаются.
    Доступ можно открыть, выполнив следующий батник

    SET DIRECTORY_NAME="C:\Windows\System32\inetsrv\config\schema"
    TAKEOWN /f %DIRECTORY_NAME% /r /d y
    ICACLS %DIRECTORY_NAME% /grant administrators:F /t
    PAUSE

    В самом файле IIS_schema.xls значение «bufferingOn» ставим false. Значения «bufferingLimit», «responseBufferLimit», «receiveBufferLimit» по умолчанию установлены 4194304. Меняем на 4096. Если ещё меньше, то IIS не запускается, тип Integer у него предполагает значение от 4096, о чём он и сообщает при попытке запуска.

    После этих исправлений в файле IIS_schema.xls, наконец, пошёл динамический вывод текста на php 5.6 на IIS8. Этим самым способом, когда один раз выполняем

    echo str_pad(' ', 1024);
    @ob_flush();
    flush();

    А потом просто flush();
    • Vitaliy Orlov Reply
      03.09.2014 в 5:29 am
      Спасибо, что поделились рецептом! Я практически не работал с конфигурацией IIS, так что особо мне нечего добавить. Однако уверен, что многим посетителям Ваш совет пригодится!
  3. Mihalch Reply
    31.08.2014 в 8:07 pm
    У меня flush(); заработал вот так:

    <?php
    echo str_pad('',1024);
    @ob_flush();
    flush();

    Это для первого вывода. А дальше где надо можно уже просто flush();
  4. Andrey Reply
    23.09.2013 в 4:03 pm
    isica, а будет ли всё это работать если нет никакого апача.
    Если Nginx и PHP как FastCGI????
  5. Максим Reply
    23.09.2013 в 4:25 am
    isica, дружище, огромное тебе спасибо, ну просто нереально огромное спасибо !
  6. isica Reply
    15.08.2013 в 11:50 pm
    Автору респект!
    Мне тоже довелось убить целый день для решения этого вопроса.
    Добавлю, что отключить буферизацию Nginx можно также прямо из php-скрипта, посредством заголовка X-Accel-Buffering.
    А вот с буферами Apache все не так гладко. Мне пришлось добавить
    while (ob_get_level()) { ob_end_flush(); }
    --без этого не работало.
    Кроме того, отключить zlib через ini_set удается не на всех серверах.

    Вот наиболее универсальный вариант:

    <?php
    header('X-Accel-Buffering: no');
    ini_set('output_buffering', 'Off');
    ini_set('output_handler', '');
    ini_set('zlib.output_handler','');
    ini_set('zlib.output_compression', 'Off');
    # ini_set('implicit_flush', 'On');
    while (ob_get_level()) { ob_end_flush(); }
    ob_implicit_flush(true);
    for ($i=1; $i<5; $i++) {
    echo $i.') Delay 2 sec';
    sleep(2);
    }
    ?>
  7. vo Reply
    21.07.2012 в 6:58 am
    Ты невнимательно прочитал статью, там никто процесс не убивает и даже не рестартует. Ему только посылается команда HUP для "обновления конфигурации". Об этом подробнее написано в мануале на сайте nginx - Управление nginx:


    Для того, чтобы nginx перечитал файл конфигурации, нужно послать главному процессу сигнал HUP. Главный процесс сначала проверяет синтаксическую правильность конфигурации, а затем пытается применить новую конфигурацию, то есть, открыть лог-файлы и новые listen сокеты. Если ему это не удаётся, то он откатывает изменения и продолжает работать со старой конфигурацией. Если же удаётся, то он запускает новые рабочие процессы, а старым шлёт сообщение о плавном выходе. Старые рабочие процессы закрывают listen сокеты и продолжают обслуживать старых клиентов. После обслуживания всех клиентов старые рабочие процессы завершаются.
  8. dreamer Reply
    21.07.2012 в 6:38 am
    О господи.. Автору позор, совет - читать маны нужно до конца, килять веб процессы не есть хорошо!

    познаём новое:
    /etc/init.d/nginx restart (server Debian based)
    /etc/init.d/php-fpm restart (после изменений php.ini, имя процесса пхп у Вас может быть и другим)

    Пишу для новичков, чтобы учились всё правильно делать..)) Удачи)

Leave a Reply

Your email address will not be published. Required fields are marked *

Allowed HTML-tags: <a>, <code>, <i>, <em>, <strong>, <b>, <u>, <strike>