Professional Apache - Apache's Performance Directives (
Page 2 of 6 )
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:
- Using Apache's core performance directives
- Configuring Apache for better performance
- Setting up Apache as a web proxy
- Enabling caching on Apache proxy servers
- Clustering web servers for reliability and performance
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:
MinSpareServers 32
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:
MaxSpareServers 64
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:
StartServers 5
MinSpareServers 5
MaxSpareServers 10
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:
MaxClients 100
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:
MaxRequestsPerChild 0
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:
MaxRequestsPerChild 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:
ThreadsPerChild 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:
ThreadsPerChild 1024
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:
SendBufferSize 32768
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:
KeepAlive on
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:
KeepAliveTimeout 15
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:
MaxKeepAliveRequests 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:
- The time since a connection being established and a GET request being
received. This does not affect persistent connections, for which
KeepAliveTimeout is used instead.
- The time since the last packet of data was received on a PUSH or PUT HTTP
request.
- The time since the last ACK (acknowledgement) response was received if the
server is waiting for more.
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:
TimeOut 300
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:
TimeOut 60
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:
ListenBacklog 1023
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:
LimitRequestBody 10240
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:
LimitRequestFields 50
LimitRequestFieldSize
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:
LimitRequestFieldSize 100
LimitRequestLine
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:
LimitRequestLine 500
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.