Назад | Перейти на главную страницу

PHP + Fcgid зависает, если загрузка прервана

Примечание: это то же самое, что и это сообщение SO, но, возможно, здесь более уместно, поскольку я подозреваю, что проблема связана с конфигурацией сервера, а не с кодом.

Я использую установку LAMP с запущенным PHP mod_fcgid. Для большинства запросов это работает хорошо, но я заметил, что когда я загружаю файл, но прерываю загрузку до ее завершения, процесс php-cgi, обслуживающий блоки файлов, пытается записать больше данных, пока не будет IPCCommTimeout достигнуто. По истечении времени ожидания процесс прерывается, и процесс снова начинает обслуживать другие запросы.

Эта проблема не возникает, если я использую mod_php вместо того mod_fcgid.

Есть ли какой-нибудь доступный параметр для fcgid, который я могу установить, чтобы он прервал его, если ничего не фиксирует вывод? Могу ли я что-нибудь сделать в PHP, чтобы справиться с этим?

Проблема не возникает, если загрузка не прерывается; Фактически, я заметил это только потому, что я пытался передать FLV-файл с помощью gddflvplayer, который, кажется, отправляет короткий запрос на получение первых нескольких кадров (которые он показывает как предварительный просмотр), а затем еще один для воспроизведения, и это вызывает та же проблема.

К вашему сведению, это часть процесса зависания cgi; он сидит так до тех пор, пока в конечном итоге не будет прерван, предположительно менеджером процесса, когда IPCCommTimeout достигнуто. Я предполагаю, что он висит, пытаясь вывести результаты readfile() вызов, но Apache больше не слушает (так как запрос был отменен пользователем).

root@some-machine:~# strace -p 24837
Process 24837 attached - interrupt to quit
write(3, "\5|A\313%\35\337\376\275\237\230\266\242\371\37YjzD<\322\215\357\336:M\362P\335\242\214\341"..., 17432

Журналы показывают, что запрос в конечном итоге получен из-за тайм-аута.

mod_fcgid: read data timeout in 240 seconds

Код загрузки более или менее просто использует readfile для обслуживания файла с несколькими задействованными заголовками (примечание: в этом коде Header более или менее просто обертка вокруг header() чтобы не было проблем в тестах).

$filepath    = '/some/path/foo.flv';
$filename    = 'foo.flv';
$disposition = 'inline';

$h = Header::get();
$h->send('Pragma: public');
$h->send('Content-Transfer-Encoding: binary');
$h->send('Content-type: ' . FileSystem::get()->getMimeType($filepath));
$h->send('Content-Length: ' . FileSystem::get()->getFileSize($filepath));
$h->send('Content-Disposition: ' . $disposition . '; filename="' . $filename . '"');
$h->send('Content-transfer-encoding: 8bit');
$h->send('Expires: 0');
$h->send('Pragma: cache');
$h->send('Cache-Control: private');

flush();
readfile($filepath);

Сам сервер работает под управлением Debian Lenny со стандартными пакетами для php5-cgi, apache2, libapache2-mod-fcgid, но я также получаю те же результаты на коробке разработки с Ubuntu 10.10.

Информация о пакете следующая -

[foo@bar ~]$  dpkg -l | egrep '(apache2|php5)'
ii  apache2-mpm-worker                  2.2.9-10+lenny9            Apache HTTP Server - high speed threaded model
ii  apache2-utils                       2.2.9-10+lenny9            utility programs for webservers
ii  apache2.2-common                    2.2.9-10+lenny9            Apache HTTP Server common files
ii  libapache2-mod-fastcgi              2.4.6-1                    Apache 2 FastCGI module for long-running CGI scripts
ii  libapache2-mod-fcgid                1:2.2-1+lenny1             an alternative module compat with mod_fastcgi
ii  php5                                5.2.6.dfsg.1-1+lenny9      server-side, HTML-embedded scripting language (metapack
ii  php5-cgi                            5.2.6.dfsg.1-1+lenny9      server-side, HTML-embedded scripting language (CGI bina
ii  php5-cli                            5.2.6.dfsg.1-1+lenny9      command-line interpreter for the php5 scripting languag
ii  php5-common                         5.2.6.dfsg.1-1+lenny9      Common files for packages built from the php5 source
ii  php5-curl                           5.2.6.dfsg.1-1+lenny9      CURL module for php5
ii  php5-ffmpeg                         0.5.3.1-3                  ffmpeg support for php5
ii  php5-fileinfo                       1.0.4-1                    Fileinfo module for PHP 5
ii  php5-gd                             5.2.6.dfsg.1-1+lenny9      GD module for php5
ii  php5-imagick                        2.1.1RC1-1                 ImageMagick module for php5
ii  php5-mysql                          5.2.6.dfsg.1-1+lenny9      MySQL module for php5
ii  php5-suhosin                        0.9.27-1                   advanced protection module for php5

Мы могли бы рассматривать вашу проблему как «на самом деле не проблема», так как при истечении времени ожидания скрипт php завершается. Если бы он не закончился по истечении тайм-аута, у вас были бы большие проблемы :-). Затем, чтобы уменьшить время зависания, вы, по крайней мере, сможете поиграть с параметрами FcgidBusyTimeout и FcgidBusyScanInterval, http://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html#fcgidbusytimeout

Теперь, по сути, apache не отправляет никакой информации о закрытии tcp / ip клиента в бэкэнд fcgid. Поиск на комета вещи на переполнении стека дает отличный ответ: https://stackoverflow.com/questions/1354690/http-proxy-fastcgi-scgi-not-closing-connection-when-client-disconnected-bug-or/1384620#1384620 , где bbum дает ссылку на патч mod-fastcgi, если вы действительно хотите справиться с преждевременным концом.

Проблема сводится к блокировке сеанса PHP; по какой-то причине mod_php удается разблокировать сеанс при отмене запроса, но mod_fcgid в этом случае нет. Вызов session_write_close() перед readfile() (100% безопасно, так как я все равно ничего не буду делать после вывода файла, так как это повредит его) обеспечивает снятие блокировки сеанса и предотвращает зависание системы для этого пользователя.

Вы можете проверить ignore_user_abort установка и max_execution_time настройка.