Apache: multi-threaded vs multi-process (pre-forked)
Apache comes in a few different flavors. The two most common are pre-forked (multi-process) and multi-threaded (worker).
Note: For an article that is more of an introduction and a how to, see How to setup Apache on your Zerigo Server.
The pre-forked flavor runs several copies of itself and each copy is designed to serve one visitor at a time. The multi-threaded model runs one, sometimes a few, copies of itself but each copy has several threads and each of those threads can serve one visitor at a time.
So, if you had 20 simultaneous requests to your server, the pre-forked version would need 20 copies of itself running to serve them all. In the same situation, the multi-threaded version might have only 1 copy running with 20 internal threads to serve the same requests.
Which one better?
Let’s outline what the pros and cons of each of the two choices are and that will hopefully help determine, based on what you’ll be serving up, which is better for you.
The multi-threaded version often faster and takes less memory. However, any additional modules running inside Apache must fully support a multi-threaded environment. Modules that are not 100% thread-safe can cause Apache to crash or behave strangely.
The pre-forked version takes more memory. In a VPS, like your Zerigo Server, memory’s usually a fairly important concern. However, the pre-forked version also alleviates the need for modules to be fully thread-safe.
One of the most common add-on modules, PHP, has some thread-safely problems. To be fair, the core of PHP is supposed to be fine in a multi-threaded Apache. However, some of the third-party libraries used by PHP are not thread-safe. This has the downside of needing to use the pre-forked version of Apache if you plan to use PHP running from inside Apache as a module (using mod_php, which is by far the most common way of running PHP).
In general, I recommend using the multi-threaded version of Apache only if you are confident that all of the rest of your software stack will support it. If it won’t, or you’re just unsure, then you should run the pre-forked version. Using more memory is definitely better than having things crash or be otherwise unstable.
When running PHP via mod_php, choose pre-forked. When running only static files (html, jpg, etc), choose multi-threaded. If passing on to a backend application server like Mongrel (for Ruby on Rails), the multi-threaded version works fine.
If you’re mixing and matching uses and even one use, in one virtual host, requires the pre-forked version, then pre-forked will need tobe your choice.
Installing each version
On Debian or Ubuntu, the pre-forked version can be installed like this:
sudo apt-get install apache2-mpm-prefork
For the multi-threaded version, install this package instead:
sudo apt-get install apache2-mpm-worker
Optimizing Apache to fit your server
Apache’s default settings for number of processes to run and maximum simultaneous clients to serve are a bit too high for most VPS situations. Unless you have one of the biggest Zerigo Servers, you’ll want to change them. Even for bigger servers it probably makes sense to review these and change them to fit your workload.
Again, on Debian and Ubuntu:
sudo nano /etc/apache2/apache2.conf
Find the section labeled “prefork”. There are 5 settings here:
StartServers 5 MinSpareServers 5 MaxSpareServers 10 MaxClients 150 MaxRequestsPerChild 0
By default, this tells Apache, when you start, create 5 copies of yourself, keep at least 5 around, don’t keep more than 10 idle servers, you may have up to 150 copies of yourself (1 to serve each client), and each child copy may live indefinitely (no maximum requests per child).
150 copies is too many for a small server. So let’s adjust these a bit:
StartServers 2 MinSpareServers 2 MaxSpareServers 6 MaxClients 20 MaxRequestsPerChild 0
The most important setting here is
MaxClients. This is the maximum number of copies of Apache that will run at once. The rest are generally just somewhat proportional to this one number.
These are fairly conservative and have high odds of working for smaller environments. It is possible, if running enough RAM-hungry stuff, that even these are too high. If you’re running a medium-to-large sized server, these may be too small.
Go ahead and save the file and exit.
As noted above, Apache’s default will use a lot of memory. Apache itself will use less here than in the pre-forked version, but it can still easily be too much.
sudo nano /etc/apache2/apache2.conf
Find the section labeled “worker”. There are 6 settings here:
StartServers 2 MaxClients 150 MinSpareThreads 25 MaxSpareThreads 75 ThreadsPerChild 25 MaxRequestsPerChild 0
In this case, Apache reads this as start 2 copies of yourself to begin with, each capable of serving 25 clients, cap the number of clients at 150 (which would be 6 copies of Apache at 25 clients each), and keep spare capacity between 25 and 75 threads, again with no limit on the lifespan of a child copy of Apache.
Let’s adjust these downward a bit:
StartServers 2 MaxClients 30 MinSpareThreads 7 MaxSpareThreads 15 ThreadsPerChild 15 MaxRequestsPerChild 0
ThreadsPerChild are the most important here as they will determine how many copies of Apache are running at once.
These are designed to work, in most cases, with even smaller servers. They are likely too conservative for larger servers in higher-traffic environments.
Save the file and exit.
After the above changes have been made, be sure to do a full restart on Apache:
sudo invoke-rc.d apache2 restart