Getting the best performance possible out of a web server is a prime concern for many administrators. Apache has a justifiably good reputation for performance, but that doesn't absolve the administrator of responsibility for making sure Apache is working at its best in their particular circumstances.
Before reading this chapter, administrators serious about performance should rebuild Apache from source on the platform on which it is to run, for the reasons described in Chapter 3. It pays to pick and choose modules carefully, only including those which are necessary, and then to build Apache statically. Not only does this remove the need for mod_so, it also makes Apache a few percent faster in operation. Coupled with platform optimizations, this can make a significant difference to Apache's performance even before other considerations.
Apache defines several directives that are directly related to performance, controlling the operation of the server at the process and protocol levels. In addition, many aspects of Apache's configuration that are not directly performance related can have either a positive or negative effect on performance, depending on how we define them. Being aware of these is an important part of configuring Apache if performance is a concern.
Rather than improving the performance of Apache itself, Apache can also be used to improve the performance of other web servers (which can also be Apache servers, of course) by being set up as an intermediate proxy, also known as a reverse proxy.
Eventually, a point will be reached when no amount of performance tuning is going to make much difference. When this point is reached, there are two possible solutions: migrate to more powerful hardware or, more interestingly, add more low-power servers and create a cluster. Apache's proxying capabilities combined with a little ingenuity in rewriting URLs give us one excellent way of creating such a cluster with very little effort.
In this chapter we will look at:
Apache's Performance Directives
Aside from general configuration issues that affect Apache's performance, which we discuss in the next section, Apache has a number of directives for tuning the server's performance in different circumstances. These fall into two main groups:Process-level directives that control the number of Apache processes (or threads, on Windows) that Apache starts and maintains as a pool to handle incoming requests.
Protocol-level directives that control how Apache manages the connection with clients and how long it will wait for activity before deciding to close a connection itself.
In turn, process-level directives divide into two groups, depending on the platform that Apache is running on. All but one is effective only on UNIX systems; the remaining one is only effective on Windows platforms.Controlling Apache Processes on UNIX
Apache runs on UNIX platforms as a pre-forking server. This means that on startup it creates a pool of child processes ready to handle incoming client requests. As requests are processed, Apache tries to make sure that there are at least a few spare servers running for subsequent request. Apache provides three directives to control the pool:StartServers <number> (default 5)
This determines the number of child processes Apache will create on startup. However, since Apache controls the number of processes dynamically depending on the server activity, this does not have very much effect, and there is not much to gain by varying it. In particular, if MinSpaceServers is higher, it will cause Apache to spawn additional processes immediately.MinSpareServers <number> (default 5)
This sets the minimum number of Apache processes that must be available at any one time; if processes become busy with client requests, Apache will start up new processes to keep the pool of available servers at the minimum value. Because of Apache's algorithm for starting servers on demand, raising this value is mostly only meaningful for handling large numbers of simultaneous requests rapidly; for sites with millions of hits per day, the following is appropriate:
MaxSpareServers <number> (default 10)
This sets the maximum number of Apache processes that can be idle at one time; if many processes are started to handle a peak in demand and then the demand tails off, this directive will ensure that excessive numbers of processes will not remain running. This value should be equal to or higher than MinSpareServers to be meaningful. Sites with a million or more hits per day can use the following as a reasonable value:
These directives used to be a lot more significant than they are now. Since version 1.3 Apache has a very responsive algorithm for handling incoming requests, starting from 1 to a maximum of 32 new processes each second until all client requests are satisfied. The objective of this is to prevent Apache starting up excessive numbers of processes all at once unless it is actually necessary because of the performance cost. The server starts with one, then doubles the number of new processes started each second, so only if Apache is genuinely experiencing a sharp rise in demand will it start multiple new processes.
The consequence of this strategy is that Apache's dynamic handling of the server pool is actually quite capable of handling large swings in demand. Adjusting these directives has little actual effect on Apache's performance on anything other than extremely busy sites, and it is usually satisfactory to stay with the default of:
Apache has another two directives related to the control of processes:MaxClients <number> (default 256)
Irrespective of how busy Apache gets, it will never create more processes than the limit set by MaxClients, either to maintain the pool of spare servers or to handle actually requests. Clients that try to connect when all processes are busy will get Server Unavailable error messages. For this reason, the value of MaxClients should not be set too low, for example:
Setting MaxClients lower helps to increase performance of client requests that succeed, at the cost of causing some client requests to fail. It is therefore a double-edged tool and indicates the server either needs to be tuned for performance more elsewhere, upgraded, or clustered.
The maximum number of clients is set to 256 by default, which is the Apache internal limit built into the server binary. To override this limit requires two things: first, ensuring that the platform will allow the process to spawn more than 256 processes, then rebuilding Apache after setting HARD_SERVER_LIMIT. The simplest way to do this is to set CFLAGS in the environment before running configure as explained in Chapter 3. However, we can also edit the src/Configuration and add it by hand to EXTRA_CFLAGS.
Note that as well as determining the maximum number of processes, MaxClients also determines the size of the scoreboard file required on some platforms (see Chapter 2 for details on the scoreboard file). Apache loads this into memory, so a large value causes Apache to use a little more memory, even if the limit is not reached.MaxRequestsPerChild <number> (default 0)
This limits the maximum number of requests a given Apache process will handle before voluntarily terminating. The object of this is to prevent memory leaks causing Apache to consume increasing quantities of memory; while Apache is well behaved in this respect the underlying platform might not be. Normally this is set to zero, meaning that processes will never terminate themselves:
This is the best value to choose if we are confident that there are no, or at least no significant, memory leaks to cause problems. (Tools such as ps, top and vmstat are useful in monitoring memory usage and spotting possible leaks).
A low value for this directive will cause performance problems as Apache will be frequently terminating and restarting processes. A more reasonable value for platforms that have memory leak problems is 1000 or 10000:
If Apache is already running enough servers according to the MinSpareServers directive this also helps to thin out the number of processes running if Apache has been through a busy period. Otherwise Apache will start a new process to make up for the one that just terminated each time a process reaches its maximum request threshold.
Ultimately the UNIX version of Apache will also run in a multi-threaded mode, at which point the ThreadsPerChild directive below will also be significant.Controlling Apache Processes on Windows
On Windows platforms, Apache does not fork; consequently, the directives for controlling the number of processes or their lifetime have no effect. Instead, Apache runs as a multi-threaded process, theoretically more efficient, although the maturity of threaded implementations on Windows platforms means that Apache is not as stable.
The number of simultaneous connections a Windows Apache server is capable of is configured with the ThreadsPerChild directive, which is analogous to both the StartServers and MaxClients directives for UNIX and defaults to 50:
Since there is only one child, this limits the number of connections to the server as a whole. For a busy site, we can raise this to a maximum of 1024, which is the limit built in to the server:
It is possible to raise this limit higher by adjusting HARD_SERVER_LIMIT as described for the MaxClients directive above.Protocol-Related Performance Directives
In addition to controlling the server pool, Apache also provides some directives to control performance-related issues at the TCP/IP and HTTP protocol levels. Since these are platform independent, they work regardless of the platform Apache is running on:SendBufferSize <bytes>
This directive determines the size of the output buffer used in TCP/IP connections and is primarily useful for queuing data for connections where the latency (that is, the time it takes for a packet to get to the remote end and for the acknowledgement message to come back) is high. For example, 32 kilobyte buffers can be created with:
Each TCP/IP buffer created by Apache will be sized to this value, one per client connection, so a large value has a significant effect on memory consumption, especially for busy sites.KeepAlive <on|off>
Persistent connections were first introduced by Netscape in the HTTP/1.0 era. HTTP/1.1 developed this idea further and used a different mechanism. Both approaches are enabled by the KeepAlive directive, which allows multiple sequential HTTP requests to be made by a client on the same connection if the client indicates that it is capable and would like to do it. The default behavior is to enable persistent connections, equivalent to:
There are few reasons to disable this; if a client is not capable of persistent connections, it will generally not ask for them. The exception is some Netscape 2 browsers that claim to be able to handle persistent connections but in fact have a bug that prevents them from detecting when Apache drops its end of the connection. For this reason, Apache ships with a default configuration that contains BrowserMatch directives to set special variables to disable persistent connections in some cases - see "Apache's Environment" in Chapter 4 for more details.
KeepAlive allows a much more rapid dialog between a client and the server to take place, at the cost of preventing the attached server process from handling any other requests until the client disconnects. To deal with this issue, Apache provides two additional directives to handle the lifetime of a persistent connection:KeepAliveTimeout <seconds>
This directive specifies the amount of time an Apache process (or thread, under Windows) will wait for a client to issue another HTTP request before closing the connection and returning to general service. This should be a relatively short value, and the default is 15 seconds, equivalent to:
This value should be a little larger than the maximum time we expect the server to spend generating and sending a response - very short for static pages, longer if the site's main purpose is dynamically generated information - plus a few seconds for the client to react. It does not pay to make this value too large. If a client does not respond in time, it must make a new connection, but it is otherwise unaffected and the server process is freed for general use in the meantime.MaxKeepAliveRequests <number>
Regardless of the time-out value, persistent connections will also automatically terminate when the number of requests specified by MaxKeepAliveRequests is reached. In order to maintain server performance, this value should be kept high, and the default is accordingly 100:
Setting this value to zero will cause persistent connections to remain active forever, so long as the time-out period is not exceeded and the client does not disconnect. This is a little risky since it makes the server vulnerable to denial-of-service attacks, so a high but finite value is preferable.TimeOut
This is a catchall directive that determines how long Apache will allow an HTTP connection to remain when it becomes apparently inactive, as determined by the following criteria:
Since these three values are rather different in nature, it is expected that they will at some point in the future become separate directives. For now they are all handled by the one value set by TimeOut. The default value for TimeOut is 5 minutes, which is equivalent to:
This is far more than should ever be necessary and is set this way because the timer is not guaranteed to be reset for every kind of activity, specifically some packet-level triggers, due to legacy code. If we're willing to accept the possible occasional disconnection, we can set this to a much lower value:
This may cause requests that genuinely take a long time to process to get disconnected if the value is set too low. File uploads performed with POST or PUT can be also be detrimentally affected by a low time-out value if we expect to upload large files across links that can suffer performance problems at peak periods (such as transatlantic connections).ListenBacklog
Connection requests from clients collect in a queue until an Apache process becomes free to service them. The maximum length of the queue is controlled with the ListenBacklog directive, which has a default value of 511. If we wanted to change it, we could use something like:
There is rarely any need to alter this value, however. If the queue is filling up because Apache is failing to process requests fast enough, performance improvements elsewhere are more beneficial than allowing more clients to queue. In addition, many operating systems will reduce this value to a system limit.HTTP Limit Directives
In addition to the protocol-related directives mentioned above, Apache supplies four directives to limit the size of the HTTP requests made by clients. These principally prevent clients from abusing the server's resources and causing denial-of-service problems and are therefore also relevant to server security. The directives are:LimitRequestBody
This limits the size of the body of an HTTP request (as sent with a PUT or POST method). The default value is zero, which translates to unlimited. The maximum value is 2147483647, or 2 gigabytes. If a client sends a body in excess of the body limit, the server responds with an error rather than servicing the request.
We can use this value to prevent abnormally large posts from clients by limiting the body size to a reasonable value. For example, we have a script that accepts input from an HTML form via PUT. We know the maximum size of the response from the filled-out form is guaranteed to be less than 10 kilobytes, so we could say:
This presumes that we don't have any other scripts on the server that might validly receive a larger HTTP request body, of course.LimitRequestFields
This limits the number of additional headers that can be sent by a client in an HTTP request, and defaults to 100. In real life, the number of headers a client might reasonably be expected to send is around 20, although this value can creep up if content negotiation is being used. A large number of headers may be an indication of a client making abnormal or hostile requests of the server. A lower limit of 50 headers can be set with:
This limits the maximum length of an individual HTTP header sent by the client, including the initial header name. The default (and maximum) value is 8190 characters. We can set this to limit headers to a maximum length of 100 characters with:
This limits the maximum length of the HTTP request itself, including the HTTP method, URL, and protocol. The default limit is 8190 characters; we can reduce this to 500 characters with:
The effect of this directive is to effectively limit the size of the URL that a client can request, so it must be set large enough for clients to access all the valid URLs on the server, including the query string sent by GET requests. Setting this value too low can prevent clients from sending the results of HTML forms to the server when the form method is set to GET.
©1999 Wrox Press Limited, US and UK.
blog comments powered by Disqus