如果下载中断,PHP + Fcgid会挂起

注意:这和这个SOpost是一样的 ,但在这里可能更合适,因为我怀疑问题是服务器configuration相关而不是代码相关。

我正在使用通过mod_fcgid运行PHP的LAMP设置。 对于大多数请求来说,这样做效果不错,但是我注意到,当我下载一个文件,但在下载完成之前中断下载,服务于文件块的php-cgi进程试图写入更多的数据,直到达到IPCCommTimeout 。 一旦达到超时,进程就会中断,进程再次开始为其他请求提供服务。

如果我使用mod_php而不是mod_fcgid则不会发生此问题。

有没有一些可用的设置fcgid,我可以设置让它中止,如果没有捕获输出? 有什么我可以用PHP来处理吗?

如果下载不中断,则不会发生该问题; 实际上,我只注意到它,因为我试图使用gddflvplayer来stream式传输FLV文件,它似乎发送一个简短的请求来获取前几帧(它显示为预览),然后是另一个播放它,这是因为一样的问题。

仅供参考,这是挂cgi过程的strace; 它一直这样坐直到它最终被中断,大概是当IPCCommTimeout到达时进程pipe理器。 我的猜测是它试图输出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 

下载代码或多或less只是使用readfile来提供文件,还包含一些头文件(注意:在这个代码中, Header或多或less只是header()以避免testing中的问题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-cgiapache2libapache2-mod-fcgid标准包,但是我也在Ubuntu 10.10的开发箱上得到了相同的结果。

包信息如下 –

 [foo @ bar〜] $ dpkg -l |  egrep'(apache2 | php5)'
 ii apache2-mpm-worker 2.2.9-10 + lenny9 Apache HTTP Server  - 高速线程模型
 ii apache2-utils 2.2.9-10 + lenny9实用程序的Web服务器
 ii apache2.2-common 2.2.9-10 + lenny9 Apache HTTP Server常用文件
 ii libapache2-mod-fastcgi 2.4.6-1用于长时间运行的CGI脚本的Apache 2 FastCGI模块
 ii libapache2-mod-fcgid 1:2.2-1 + lenny1与mod_fastcgi替代模块compat
 ii php5 5.2.6.dfsg.1-1 + lenny9服务器端,HTMLembedded式脚本语言(metapack
 ii php5-cgi 5.2.6.dfsg.1-1 + lenny9服务器端,HTMLembedded式脚本语言(CGI bina
 ii php5-cli 5.2.6.dfsg.1-1 + lenny9 php5脚本语言的命令行解释器
 ii php5-common 5.2.6.dfsg.1-1 + lenny9从php5源码build立的软件包的常见文件
 ii php5-curl 5.2.6.dfsg.1-1 + lenny9用于php5的CURL模块
 ii php5-ffmpeg 0.5.3.1-3 ffmpeg支持php5
 ii php5-fileinfo 1.0.4-1用于PHP 5的Fileinfo模块
 ii php5-gd 5.2.6.dfsg.1-1 + lenny9用于php5的GD模块
 ii php5-imagick 2.1.1RC1-1用于php5的ImageMagick模块
 ii php5-mysql 5.2.6.dfsg.1-1 + lenny9用于php5的MySQL模块
 ii php5-suhosin 0.9.27-1用于php5的高级保护模块

我们可以看到你的问题是“不是一个真正的问题”,因为当超时发生php脚本结束。 如果超时没有结束,你会遇到更大的问题:-)。 然后,为了减less挂起时间,您至less可以使用FcgidBusyTimeout&FcgidBusyScanInterval参数, http ://httpd.apache.org/mod_fcgid/mod/mod_fcgid.html#fcgidbusytimeout

现在,有效的apache不会发送任何关于客户端TCP / IP封闭的信息到fcgid后端。 在堆栈溢出search彗星的东西给了这个优秀的响应: https : //stackoverflow.com/questions/1354690/http-proxy-fastcgi-scgi-not-closing-connection-when-client-disconnected-bug-or/1384620 #1384620 ,如果你真的想要处理过早结束的东西,那么bbum会给出一个mod-fastcgi补丁的链接。

问题归结为PHP会话locking; 出于某种原因,mod_php设法在请求被取消时解锁会话,但mod_fcgid不在这种情况下。 在readfile() session_write_close()之前调用session_write_close() (100%安全,因为我不会在输出文件之后做任何事情,因为它会破坏它)确保会话锁被释放,并防止系统挂起该用户。

您可能需要检查ignore_user_abort设置和max_execution_time设置。