July 2011 Note: mod_fcgid has changed its behavior in opening and closing processes since I wrote this article. mod_fcgid is now an Apache project, and limiting it to 4 processes with the configuration I have below caused me problems when I migrated to a server with the newer mod_fcgid. mod_fcgid now opens a new process for each "class" which for our php purposes means each .php file location requested by a URL, and it tries to keep 3 processes open for each class by default. Since I limited the max processes to 4, it will not close enough idle processes to allow new classes to open processes, and the pages fail. I think this may be fixable with settings, but I had other upgrade frustrations during the migration and am not now constrained by a small VPS, so I haven't yet been motivated to figure out how to get the newer mod_fcgid to operate well in a small VPS.
My best-performing small VPS setup was with lighttpd and FastCGI PHP, but I got tired of trying to make rewrites work in lighttpd and switched to a two-process prefork Apache with mod_php and Squid as a web accelerator. That worked pretty well, but not as fast as lighttpd and FastCGI. What I really want is a multithreaded Apache and a FastCGI PHP that will fit in my small, cheap VPS.
I had tried Apache's worker MPM and FastCGI before, but at the time both Apache and the PHP FastCGI process bloated and took up all my RAM despite my settings. Recently I decided to try again and was able to find out how to make it work.
Under Linux, by default each thread is assigned 8MB of stack memory, so an Apache process with 25 threads would try to take up 25*8=200MB of RAM!!! Plus the size of the Apache parent process, plus anything else that runs on my VPS. Not going to work in my small VPS. However, each thread doesn't really need that much RAM. In fact, 128k is working fine for me so far. Apache 2.2 has a new directive ThreadStackSize for the worker MPM, and I set mine to "ThreadStackSize 131072", and now I can have two Apache processes with 25 threads each taking up about 25MB worth of privvmpages. Another way to accomplish this is add "ulimit -s 128" to the Apache startup scripts. For Apache 2.0 you have to do it this way. Since I am using Apache 2.2 I didn't have to use ulimit, but when I was testing the effects of changing the stack size I used this script which worked as a temporary measure:
My 25 MB RAM usage above is without mod_php, though. Google searches lead to conflicting information about whether PHP is thread safe, so I want to use FastCGI. My problem with Apache FastCGI before was that it spawned several times as many PHP processes as I thought I had told it to. I was using mod_fcgid and pointing it to the same FastCGI PHP wrapper script that I had used for lighttpd. But that script set PHP to launch child processes, and I have since learned that mod_fcgid does not multiplex and therefore will not use the child processes. Instead it launches as many processes as it sees fit, and my configuration had each of those launching 4 children. No wonder my RAM got chewed up so quickly. So now I am letting mod_fcgid call /usr/bin/php-cgi directly:
Unfortunately, since each PHP processes is launched separately, any caching such as eAccelerator or APC will not be shared across each process. And each process uses up another X MB of RAM for the cache. So if I'm using APC with the default 30 MB cache and have 4 FastCGI PHP processes going, my APC caches are taking up 120 MB all by themselves! At the moment this is exactly what I'm doing, because I've moved up from a 256 MB VPS to a 390 MB VPS, and my total memory usage seems to be hovering near 256 MB when all processes are running. However, when PHP processes aren't needed, mod_fcgid will kill them off to save memory, so most of the time I'm using much less RAM. I will see if I can set up the FastCGI processes like I did with lighttpd and then connect to it from Apache. I think it's doable, but I haven't tried yet.
I like using the worker MPM and FastCGI better than using the prefork MPM, mod_php and Squid. First of all the log files are a lot easier to parse. Apache (as I have it configured) can handle 50 concurrent static requests including 4 concurrent PHP requests, and I was able to enable KeepAlives again so my sites feel more responsive. With prefork Apache and Squid I could see a row of GIF smileys load up left-to-right when posting a reply in one of my forums. Now it happens so fast I can't see it anymore. If I can get the PHP cache--previously eAccelerator, but I just switched to APC--to share itself across all my PHP processes then the overal RAM usage will be much lower, and I'll be able to have more PHP processes.
And I like the new setup better than lighttpd because I can use the Apache rewrite rules provided by software programs like Drupal and SMF rather than try to translate them into lighttpd rewrites.