PHP + Fcgid trava se o download foi interrompido

1

Observação: isso é o mesmo que esta postagem SO , mas é possivelmente mais apropriado aqui, pois suspeito que o problema esteja relacionado à configuração do servidor em vez de ao código.

Estou usando uma configuração LAMP com o PHP em execução em mod_fcgid . Para a maioria das solicitações, isso funciona bem, mas notei que quando eu baixo um arquivo, mas o interrompo antes que ele seja concluído, o processo php-cgi que estava servindo os blocos de arquivo tenta gravar mais dados até atingir IPCCommTimeout . Quando o tempo limite é atingido, o processo é interrompido e o processo começa a atender outras solicitações novamente.

Esse problema não ocorre se eu usar mod_php em vez de mod_fcgid .

Existe alguma configuração disponível para o fcgid que eu possa configurar para abortar se nada estiver capturando a saída? Existe algo que eu possa fazer no PHP para lidar com isso?

O problema não ocorre se o download não for interrompido; na verdade, eu só notei porque estava tentando transmitir um arquivo FLV usando o gddflvplayer, que parece enviar uma solicitação rápida para obter os primeiros quadros (que é exibido como uma prévia), depois outro para reproduzi-lo, e isso causa o mesmo problema.

FYI, esta é a sequência do processo cgi suspenso; fica assim até que seja eventualmente interrompido, presumivelmente pelo gerenciador de processos quando o IPCCommTimeout é atingido. Meu palpite é que ele está pendurado tentando mostrar os resultados da chamada readfile() , mas o Apache não está mais atendendo (já que a solicitação foi cancelada pelo usuário).

root@some-machine:~# strace -p 24837
Process 24837 attached - interrupt to quit
write(3, "|A3%76570621YjzD<2576:M2P5241"..., 17432

Os registros indicam que a solicitação é eventualmente obtida devido ao tempo limite

mod_fcgid: read data timeout in 240 seconds

O código de download mais ou menos usa apenas readfile para exibir o arquivo, com alguns cabeçalhos envolvidos também (observação: neste código, Header é mais ou menos apenas um wrapper em torno de header() para evitar problemas nos testes).

$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);

O próprio servidor está executando o Debian Lenny com pacotes padrão para php5-cgi , apache2 , libapache2-mod-fcgid , mas eu também obtenho os mesmos resultados em uma caixa de desenvolvimento com o Ubuntu 10.10.

A informação do pacote segue -

[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
    
por El Yobo 25.01.2011 / 04:05

3 respostas

1

O problema se resume ao bloqueio de sessão do PHP; por alguma razão o mod_php consegue desbloquear a sessão quando o pedido é cancelado, mas o mod_fcgid não faz neste caso. Chamar session_write_close() antes de readfile() (100% seguro, já que não farei nada após a saída do arquivo, pois isso o corromperia) garante que o bloqueio de sessão seja liberado e evita que o sistema fique suspenso por esse usuário.

    
por 23.02.2011 / 09:12
2

Poderíamos ver o seu problema como "não é realmente um problema", pois quando ocorre o tempo limite, o script php termina. Se não estivesse terminando após o tempo limite, você teria problemas maiores :-). Então, para reduzir o tempo de travamento, você poderia ao menos brincar com o FcgidBusyTimeout & Parâmetros FcgidBusyScanInterval, link

Agora, efetivamente o apache não envia nenhuma informação sobre o encerramento tcp / ip do cliente para o fcgid backend. Uma pesquisa nas coisas do comet no Stack overflow dá uma excelente resposta: link , onde o bbum dá um link para um patch mod-fastcgi, se você realmente quer lidar com as coisas finais prematuras.

    
por 03.01.2011 / 20:53
0

Você pode querer verificar a configuração ignore_user_abort e a max_execution_time configuração.

    
por 03.01.2011 / 11:23