Буферизация вывода и flush
Столкнулся с проблемой, которая не дает мне покоя, уже не первый раз. Но вот пришло время её решить.
Не работает flush(); на хостинге заказчика.
Тестовый пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?php error_reporting(-1); ini_set('display_errors','On'); ini_set('output_buffering', 'Off'); ini_set('output_handler', ''); ini_set('zlib.output_compression', 'Off'); ini_set('implicit_flush', 'On'); ob_implicit_flush(true); for ($i=1; $i<5; $i++) { echo $i.') Delay '.($i*2).' sec<br />'; flush(); sleep($i*2); } ?> |
Есть доступ к конфигурации php.ini
1 2 3 4 5 6 7 8 |
output_buffering = Off ;output_handler = zlib.output_compression = Off ;zlib.output_handler = implicit_flush = Off safe_mode = Off |
PHP работает как модуль Апача.
Работающие модули ISP Manager
12345678910111213141516 ZendExtensionManager.so bz2.so calendar.socgi-fcgi.so ctype.so curl.sodate.so dbase.so eaccelerator.soexif.so filter.so ftp.sogd.so gettext.so gmp.sohash.so iconv.so json.solibxml.so mbstring.so mysql.somysqli.so openssl.so pcntl.sopcre.so pdo.so pdo_mysql.sopdo_sqlite.so readline.so zlib.soreflection.so session.so shmop.sosimplexml.so sockets.so spl.sostandard.so tokenizer.so xml.sozip.so
PHP Info:
Configure command:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
./configure' '--build=x86_64-redhat-linux-gnu' '--host=x86_64-redhat-linux-gnu' '--target=x86_64-redhat-linux-gnu' '--program-prefix=' '--prefix=/usr' '--exec-prefix=/usr' '--bindir=/usr/bin' '--sbindir=/usr/sbin' '--sysconfdir=/etc' '--datadir=/usr/share' '--includedir=/usr/include' '--libdir=/usr/lib64' '--libexecdir=/usr/libexec' '--localstatedir=/var' '--sharedstatedir=/usr/com' '--mandir=/usr/share/man' '--infodir=/usr/share/info' '--cache-file=../config.cache' '--with-libdir=lib64' '--with-config-file-path=/etc' '--with-config-file-scan-dir=/etc/php.d' '--disable-debug' '--with-pic' '--disable-rpath' '--without-pear' '--with-bz2' '--with-exec-dir=/usr/bin' '--with-freetype-dir=/usr' '--with-png-dir=/usr' '--with-xpm-dir=/usr' '--enable-gd-native-ttf' '--with-t1lib=/usr' '--without-gdbm' '--with-gettext' '--with-gmp' '--with-iconv' '--with-jpeg-dir=/usr' '--with-openssl' '--with-pcre-regex' '--with-zlib' '--with-layout=GNU' '--enable-exif' '--enable-ftp' '--enable-magic-quotes' '--enable-sockets' '--enable-sysvsem' '--enable-sysvshm' '--enable-sysvmsg' '--with-kerberos' '--enable-ucd-snmp-hack' '--enable-shmop' '--enable-calendar' '--without-mime-magic' '--without-sqlite' '--with-libxml-dir=/usr' '--with-xml' '--with-system-tzdata' '--with-apxs2=/usr/sbin/apxs' '--without-mysql' '--without-gd' '--disable-dom' '--disable-dba' '--without-unixODBC' '--disable-pdo' '--disable-xmlreader' '--disable-xmlwriter' '--disable-json' '--without-pspell' '--disable-wddx' '--without-curl' '--disable-posix' '--disable-sysvmsg' '--disable-sysvshm' '--disable-sysvsem' |
Core:
1 2 3 4 |
output_buffering no value no value output_handler no value no value |
Zlib:
1 2 3 4 5 |
zlib.output_compression Off Off zlib.output_compression_level -1 -1 zlib.output_handler no value no value |
Пошел я в гугл, долго искал, читал и пробовал.. Ничего не помогает.. Я грешил на какую-то буферизирующую софтинку, и решил в очередной раз проверить, не загоняет ли сервак свои ответы в gzip, т.к. это обозначало бы что они кешируются.. И вот что я увидел
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
GET /module/cron/test.php HTTP/1.1 Host: site.ru User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ru; rv:1.9.2.8) Gecko/20100722 Firefox/3.6.8 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language: ru,en-us;q=0.7,en;q=0.3 Accept-Encoding: gzip,deflate Accept-Charset: windows-1251,utf-8;q=0.7,*;q=0.7 Keep-Alive: 115 Connection: keep-alive Cookie: conn[db]=db; conn[user]=user; conn[pwd]=pass; dle_newpm=0; PHPSESSID=blnpk7ijpvrnumobn443aomvr7 Cache-Control: max-age=0 HTTP/1.1 200 OK Server: nginx/0.7.65 Date: Sat, 18 Sep 2010 10:04:03 GMT Content-Type: text/html; charset=cp1251 Transfer-Encoding: chunked Connection: keep-alive Keep-Alive: timeout=20 X-Powered-By: PHP/5.2.14 |
Кеширования нет, но .. Server: nginx
Вот оно что, в качестве фронтенда, стоял nginx, и я заподозрил именно его в таком поведении.
Нашел конфиг, он находился в /etc/nginx/nginx.conf, посмотрел там, и нашел вот это:
1 2 3 4 5 6 |
proxy_buffer_size 4k; proxy_buffers 120 64k; proxy_busy_buffers_size 64k; proxy_temp_file_write_size 64k; |
С конфигурацией nginx я столкнулся впервые, знал лишь то, что разработал его Игорь Сысоев и он русский, а это значит, что должно быть достаточно мануалов. Пошел в поиск и попал к нему на сайт, там меня интересовал раздел документации, а именно модуль ngx_http_proxy_module. Вот там я и нашел ответ на свой вопрос, а именно директиву proxy_buffering:
Директива разрешает использовать буферизацию ответа проксируемого сервера. Если буферизация включена, то nginx принимает ответ проксируемого сервера как можно быстрее, сохраняя его в буфера, заданные директивами proxy_buffer_size и proxy_buffers. Если ответ не помещается полностью в память, то его часть записывается на диск.
Если буферизация выключена, то ответ синхронно передаётся клиенту сразу же по мере его поступления. nginx не пытается считать весь ответ проксируемого сервера, максимальный размер данных, который nginx может принять от сервера задаётся директивой proxy_buffer_size.
Пошел искать эту директиву в своем конфиге, но её там небыло. Ну, раз нет, тогда добавим и я её прописал в секции Main location:
1 2 3 |
proxy_buffering off; |
(!) Не забудьте про точку запятую в конце. Я забыл, и в начале подумал, что это лекарство мне не помогло.
Конфигурация, сохранена. Остается вопрос, как же рестартануть 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: | Tags: /
| Rating:
10 comments.
Write a comment