xgrr.de – the whole not the half

things, thoughts and stuff out of life, daily business and computer science


Apache2 + mod_fcgid + PHP = Awesome

For years now I was using PHP as a Apache module and not thinking twice about it. Easy to install not much to maintain and working reasonably well. Server too slow? Buy a new server. Recently one of our servers got really slow again with varying timeouts while serving pages. Memory shortage. No new server available I needed to make the most of it and started investigating how to decrease the memory footprint of Apache.

FastCGI was the answer. With PHP as a module installed every Apache process can take up to the maximum amount of memory you defined in php.ini plus what the Apache process needs by himself even though this process is only serving static content. With the switch to FastCGI it was possible to use the threaded version of Apache (mpm_worker). Static contents are now served really fast without need for invoking PHP at all. PHP scripts are served by calling the FastCGI IPC.

As memory is still low Apache and PHP are both configured to “die” after quite a low number of requests served. This can sometimes lead to a small delay while the processes are restarted but ensures that the memory footprint is kept low.

How did I do it? I won’t go into details on how to install Apache, mod_fcgid and PHP because there are loads of howto’s out there. Please find the configuration I’m using below. I’m using Debian and Apache, mod_fcgid and PHP are installed out of the repository. If you have a custom compiled version or different distribution your paths can differ.

Depending on your available hardware it is feasible to tweak the settings for the mpm_worker module so more concurrent clients can be served and/or the amount of requests which is handled before re-creating the thread is higher. It is also very recommended to use eAccelerator in conjunction with this setup. See my post on the topic for more information.

/etc/apache2/apache2.conf (excerpt):

<IfModule mpm_worker_module>
	StartServers           4
	ServerLimit            4
	MaxClients           128
	MinSpareThreads        8
	MaxSpareThreads       16
	ThreadsPerChild       32
	MaxRequestsPerChild  500
</IfModule>

/etc/apache2/mods-available/fcgid.conf:

<IfModule mod_fcgid.c>
	AddHandler fcgid-script .fcgi .php
	FCGIWrapper /var/www/php-fcgi-starter .php

	IdleTimeout		3600
	BusyTimeout		 300
	ProcessLifeTime		7200
	IPCConnectTimeout	  10
	IPCCommTimeout		 360
	MaxProcessCount		  15
	MaxRequestsPerProcess	  -1
	PHP_Fix_Pathinfo_Enable    1
</IfModule>

/var/www/php-fcgi-starter:

#!/bin/sh
PHPRC=/etc/php5/cgi/
export PHPRC
export PHP_FCGI_MAX_REQUESTS=250
export PHP_FCGI_CHILDREN=1
exec /usr/lib/cgi-bin/php

Comparing Roadsend PHP to PHP/FastCGI

For some projects it might come in handy to have a pre-compiled binary shipped to the customer or run as a separate instance. I took a peek at Roadsend PHP which implemented their own engine and is able to compile PHP source into C binaries which can either run on the command line or with special compilation arguments as a FastCGI program.

But before using it productively and changing a lot of source to make it compatible with the way how Roadsend works I wanted to make sure that the promised performance bonus would be really there. I created a very crude script which does nothing more than to iterate an integer to a certain maximum and measures the time to do this.

NOTE: I know that a proper performance test looks differently and I’m aware that Roadsend PHP can put his design into the works when we deal with a lot of includes. I’ll make another test with including random files to compare if this is impacting the results.

<?php
echo “Simple iteration test to compare performance between interpreted PHP and Roadsend PHP<hr />”;

$max = 1000;
if($_GET["max"] != “” && is_numeric($_GET["max"])) {
$max = $_GET["max"];
}

$start = microtime();

for($i = 0; $i < $max; $i++) {
echo $i.”<br />”;
}

$stop = microtime();
$elapsed = $stop – $start;
echo “<hr />Script took $elapsed seconds to execute”;
?>

To the environment. I didn’t set up a clean room environment. I used my server at home with 2G of memory Athlon64 X2 and a RAID5. Nothing special but I think it resembles real-world situations perfectly (unless you setup a new box for every PHP site you build).

Apache (worker – threaded) is used for serving the requests. FastCGIs are served via mod_fcgid. PHP is also run via mod_fcgid which improved the performance of PHP dramatically so far.

Let’s get to it:

max = 1000
Roadsend PHP: 0.005817
Interpreded PHP: 0.001201

max = 10000
Roadsend PHP: 0.06846
Interpreded PHP: 0.012625

These results are not dramatically but significant. It seems that compiling PHP adds overhead to the execution.

PHP 5.3 and namespaces

Java and .NET (I suppose even more languages…) developers already know the trick with namespaces. But with the upcoming version 5.3 of PHP they can use namespaces to group their classes too.

The development of my current projects made me think to write libraries which I can use in all of my projects. Due to a few limitations (no typing, no typecasting etc.) this is problematic. So I build a framework which provides me with a standard libraries. So I have to maintain only this library for all the projects which uses it.

For a special case I wanted to interconnect two of the projects. This was impossible because the two projects are setup identically. This means they both have classes called Database etc. With PHP 5.3 this is no problem anymore because I can give the two projects different namespaces.