PHP readfile()永远不会结束并使Apache服务器挂起

hed*_*edi 9 php apache preforking

我有一个apache服务器,php应用程序的大问题.

该服务器正在为一个运行速度很快的网站提供服务.

每24小时或48小时,apache挂起,我必须重新启动才能再次访问该网站.我必须重新启动它,因为apache达到允许的进程/服务器的最大数量(对我来说是16000),并且它不能释放其他进程,因为其他进程都是活动的.

托管在这个服务器上的网站是一个php应用程序,最后提供一个文件:假设它是一个下载服务器.

浏览器通过提交POST请求的表单请求文件.

问题是这个帖子请求似乎永远不会结束(我可以看到我的服务器状态上的几乎所有16000个进程都是POST请求).

提供的文件是大文件(10M到2G),我使用php readfile函数为它们服务(我不想用href链接提供它们,所以我使用表单POST请求,以便用户永远不会看到文件在我的文件系统上).

使用php readfile的函数似乎永远不会结束,即使我在它的末尾使用exit()(参见下面的代码snipet).

我在这里要求一种方法来避免这种永远不会结束由我的PHP代码引起的POST请求.我想保持POST方式提供文件.

首先我的conf:

  • Ubuntu服务器14.04
  • apache 2.4与mpm prefork
  • php 5.5.9(mod php)
  • 硬件:128G RAM

我的mpm_prefork.conf文件:

<IfModule mpm_prefork_module>
        StartServers              512
        MinSpareServers           512
        MaxSpareServers          1024
        ServerLimit             16000 # no problem with my server ram
        MaxRequestWorkers       16000
        MaxConnectionsPerChild  10000
</IfModule>
Run Code Online (Sandbox Code Playgroud)

我的apache2.conf文件:

...
Timeout 300
KeepAlive On
MaxKeepAliveRequests 500
KeepAliveTimeout 5
...
Run Code Online (Sandbox Code Playgroud)

我的php.ini文件:

max_execution_time = 7200
Run Code Online (Sandbox Code Playgroud)

我的apache日志文件:对我的问题没什么好处的

显示问题何时发生的munin图: 在此输入图像描述

我的apache服务器状态如下: 在此输入图像描述

在此输入图像描述

和我的服务器类(导致问题的代码):

class Server
{
    /* the file is served from a remote url source */
    public function serveFileFromUrl()
    {
        if (empty($_POST)) {
            return;
        }

        $url  = $_POST['file_url'];
        $mime = $_POST['mime'];
        $name = sanitizeFileName($_POST['name']) . uniqid() . '.' . $mime;
        $size = $_POST['size'];

        if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false) {

            // for Internet Explorer
            if ($mime == 'mp3') {
                header('Content-Type: "audio/' . $mime . '"');
            } else {
                header('Content-Type: "video/' . $mime . '"');
            }
            header('Content-disposition: attachment; filename="' . $name . '"');
            header('Expires: 0');
            if ($size !== '') {
                header('Content-Length: ' . $size);
            }
            header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
            header("Content-Transfer-Encoding: binary");
            header('Pragma: public');

        } else {

            // not for internet Explorer
            if ($mime == 'mp3') {
                header('Content-Type: "audio/' . $mime . '"');
            } else {
                header('Content-Type: "video/' . $mime . '"');
            }
            header('Content-disposition: attachment; filename="' . $name . '"');
            header('Expires: 0');
            if ($size !== '') {
                header('Content-Length: ' . $size);
            }
            header("Content-Transfer-Encoding: Binary");
            header('Pragma: no-cache');

        }

        ob_end_clean(); // fix memory problems with readfile (http://heap.tumblr.com/post/119127049/a-note-about-phps-output-buffer-and-readfile)
        flush();        // fix memory problems with readfile
        readfile($url); 
        @ob_end_flush();
        exit();
    }

    /* file is served from my filesystem */
    public function serveFileFromPath()
    {
        if (empty($_POST)) {
            return;
        }

        $url  = APP_PATH . '/download/' . $_POST['file_name'];
        $mime = $_POST['mime'];
        $name = sanitizeFileName($_POST['name']) . '-' . uniqid() . '.' . $mime;
        $size = $_POST['size'];

        if (strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false) {

            // for Internet Explorer
            if ($mime == 'mp3') {
                header('Content-Type: "audio/' . $mime . '"');
            } else {
                header('Content-Type: "video/' . $mime . '"');
            }
            header('Content-disposition: attachment; filename="' . $name . '"');
            header('Expires: 0');
            if ($size !== '') {
                header('Content-Length: ' . $size);
            }
            header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
            header("Content-Transfer-Encoding: binary");
            header('Pragma: public');

        } else {

            // not for internet Explorer
            if ($mime == 'mp3') {
                header('Content-Type: "audio/' . $mime . '"');
            } else {
                header('Content-Type: "video/' . $mime . '"');
            }
            header('Content-disposition: attachment; filename="' . $name . '"');
            header('Expires: 0');
            if ($size !== '') {
                header('Content-Length: ' . $size);
            }
            header("Content-Transfer-Encoding: Binary");
            header('Pragma: no-cache');

        }

        ob_end_clean(); // fix memory problems with readfile (http://heap.tumblr.com/post/119127049/a-note-about-phps-output-buffer-and-readfile)
        flush();        // fix memory problems with readfile
        readfile($url);
        @ob_end_flush();
        exit();
    }
}
Run Code Online (Sandbox Code Playgroud)

有人有解决方案来避免永无止境的POST请求吗?如果它可以解决问题,我可以通过PHP之外的其他东西来提供文件.

请不要重复,我已经添加了足够的代码,conf片段和图片,以使这个问题具体:)

use*_*121 3

mod_xsendfile 是 PHP 传送文件的一个很好的替代方案。

https://tn123.org/mod_xsendfile/

否则,您可以简单地向 PHP 脚本添加时间限制,这样它就不能永远运行。

http://php.net/manual/de/function.set-time-limit.php