9

I have a big problem with an apache server, php app.

The server is serving a quite high traffic website which is running with php.

Every 24h or 48h, apache hangs, and i have to reboot it to be able to access the website again. I have to reboot it, because apache reaches the maximum number of allowed processes/servers (16000 for me), and it cannot free other processes because other processes are all active.

The website hosted on this server is a php app, that serve a file at the end: let say it is a download server.

Files are requested by the browser via a form that submit a POST request.

The problem is that this post request seems to never end (i can see that nearly all the 16000 processes on my server status are POST requests).

Files that are served are big files (10M to 2G), and i serve them with php readfile function (i don't want to serve them with a href link, so i use a form POST request so that the user never see where the file is on my filesystem).

The function that use php readfile seems to never end even if i use exit() at the end of it (see code snipet below).

I am asking here for a way to avoid this never ending POST requests that are caused by my php code. I want to keep POST way of serving files.

First my conf:

  • Ubuntu server 14.04
  • apache 2.4 with mpm prefork
  • php 5.5.9 (mod php)
  • hardware: 128G RAM

my mpm_prefork.conf file:

<IfModule mpm_prefork_module>
        StartServers              512
        MinSpareServers           512
        MaxSpareServers          1024
        ServerLimit             16000 # no problem with my server ram
        MaxRequestWorkers       16000
        MaxConnectionsPerChild  10000
</IfModule>

my apache2.conf file:

...
Timeout 300
KeepAlive On
MaxKeepAliveRequests 500
KeepAliveTimeout 5
...

My php.ini file:

max_execution_time = 7200

my apache log files: nothing interesting for my problem

A munin graph that shows when the issue happens: enter image description here

My apache server status at looks like that: enter image description here

enter image description here

And my server class (the code that is causing the issue):

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();
    }
}

Does someone has a solution to avoid the never ending POST requests? I am ok to serve files via other thing than php if it can solve the issue.

Please no duplicate, i have added enough code, conf snippet and pictures to make this question specific :)

Martin Thorsen Ranang
  • 2,394
  • 1
  • 28
  • 43
hedi
  • 1,476
  • 2
  • 11
  • 14
  • 2
    an easy way to get rid of 50% of that code would be to replace the serveFileFromUrl() part with an http redirect, aka header Location. it might even fix your problem with any luck. – Calimero Nov 23 '15 at 08:53
  • 1
    You have set your timelimit to 0. Dont. tis is definetly not a fix, but set it something like 10 hours, it will keep your server alive even if something fails like it does now and it, most likely, wont mess with what you do. Set it double the time you think a script would take. – Elentriel Nov 23 '15 at 08:58
  • @Calimero if i use only header Location, the file (this is a video file) is played in the browser. I want the file to be downloaded. – hedi Nov 23 '15 at 09:00
  • @Elentriel ok, i have modified it to 14400 (4 hours), and i'll see if it helps. thanks – hedi Nov 23 '15 at 09:08
  • Try adding `error_reporting(-1);`. What's calling your Server class, do we know for sure it's even getting there and not hanging before then? – Drazisil Nov 23 '15 at 09:29
  • @Drazisil My app is already debugged, and has no errors. For sure we are getting there, the app is working and files are served to the browser correctly. – hedi Nov 23 '15 at 09:35
  • In one of my projects I stumbled into similar problems and could fix them by using readfile_chunked() function described at http://php.net/manual/en/function.readfile.php – maxhb Nov 23 '15 at 09:40

1 Answers1

3

mod_xsendfile is a good alternative to PHP delivering files.

https://tn123.org/mod_xsendfile/

Else you could simply add a timelimit to your PHP script, so it cannot run forever.

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

user5542121
  • 1,051
  • 12
  • 28
  • max_execution_time is already set to 2 hours in my php.ini file. i updated the question with it. – hedi Nov 23 '15 at 09:04
  • You should really consider mod_xsendfile for your use case. Another read about it: http://codeutopia.net/blog/2009/03/06/sending-files-better-apache-mod_xsendfile-and-php/ – user5542121 Nov 23 '15 at 09:13
  • I'll take a look, thanks. I tried it 2 weeks ago unsuccessfully: it was serving 0kb files. – hedi Nov 23 '15 at 09:19
  • 1
    Well I marked your answer as accepted, because i reinstalled my server with nginx and i now use x-accel-redirect which is the nginx equivalent of apache mod_xsendfile. No more problem now. thanks – hedi Dec 06 '15 at 13:07
  • 1
    max_execution_time does not include in its calculations the network and DB calls. – Nikolay Shindarov Feb 11 '19 at 11:35