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

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

注意:这与这篇文章,但在这里可能更合适,因为我怀疑问题与服务器配置有关,而不是与代码有关。

我使用的是 LAMP 设置,其中 PHP 正在运行mod_fcgid。对于大多数请求,这都运行良好,但我注意到,当我下载文件但在完成之前中断下载时,为文件提供服务的 php-cgi 进程会阻止尝试写入更多数据,直到达到超时时间IPCCommTimeout。一旦达到超时时间,进程就会中断,然后进程会再次开始为其他请求提供服务。

mod_php如果我使用而不是,则不会出现此问题mod_fcgid

是否有一些可用的 fcgid 设置,我可以设置它以在未捕获任何输出时中止?我可以在 PHP 中做些什么来处理它吗?

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

仅供参考,这是挂起的 cgi 进程的 strace;它一直处于这种状态,直到最终被中断,大概是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 服务器 - 高速线程模型
ii apache2-utils 2.2.9-10+lenny9 网络服务器实用程序
ii apache2.2-common 2.2.9-10+lenny9 Apache HTTP 服务器通用文件
ii libapache2-mod-fastcgi 2.4.6-1 Apache 2 FastCGI 模块,用于长时间运行的 CGI 脚本
ii libapache2-mod-fcgid 1:2.2-1+lenny1 与 mod_fastcgi 兼容的替代模块
ii php5 5.2.6.dfsg.1-1+lenny9 服务器端,HTML 嵌入式脚本语言 (metapack
ii php5-cgi 5.2.6.dfsg.1-1+lenny9 服务器端,HTML 嵌入式脚本语言 (CGI bina
ii php5-cli 5.2.6.dfsg.1-1+lenny9 php5 脚本语言的命令行解释器
ii php5-common 5.2.6.dfsg.1-1+lenny9 从 php5 源代码构建的软件包的通用文件
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 高级防护模块

答案1

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

现在,实际上 apache 不会向 fcgid 后端发送任何有关客户端 tcp/ip 关闭的信息。搜索彗星Stack overflow 上的事情给出了很好的回应:https://stackoverflow.com/questions/1354690/http-proxy-fastcgi-scgi-not-closing-connection-when-client-disconnected-bug-or/1384620#1384620,如果您确实想要处理过早结束的事情,其中​​ bbum 提供了一个 mod-fastcgi 补丁的链接。

答案2

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

答案3

您可能需要检查忽略用户中止设置和最大执行时间环境。

相关内容