title: Choosing the Right Strategy

=head1 Do it like I do it!?

There is no such thing as the B<RIGHT> strategy in the web server
business, although there are many wrong ones. Never believe a person
who says: I<"Do it this way, this is the best!">. As the old saying
goes: I<"Trust but verify">. There are too many technologies out there
to choose from, and it would take an enormous investment of time and
money to try to validate each one before deciding which is the best
choice for your situation.

With this in mind, I will present some ways of using standalone
mod_perl, and some combinations of mod_perl and other technologies.
I'll describe how these things work together, and offer my opinions on
the pros and cons of each, the relative degree of difficulty in
installing and maintaining them, and some hints on approaches that
should be used and things to avoid.

To be clear, I will not address all technologies and tools, but limit
this discussion to those complementing mod_perl.

Please let me stress it again: B<DO NOT> blindly copy someone's setup
and hope for a good result. Choose what is best for your situation --
it might take B<some> effort to find out what that is.

In this chapter we will discuss

=over

=item * Deployment of mod_perl in Overview, with the pros and cons.

=item * Alternative architectures for running one and two servers.

=item * Proxy servers (Squid, and Apache's mod_proxy).

=back






=head1 mod_perl Deployment Overview

There are several different ways to build, configure and deploy your
mod_perl enabled server. Some of them are:

=over 8

=item 1

Having one binary and one configuration file (one big binary for mod_perl).

=item 2

Having two binaries and two configuration files (one big binary for mod_perl
and one small binary for static objects like images.)

=item 3

Having one DSO-style binary and two configuration files, with mod_perl
available as a loadable object.

=item 4

Any of the above plus a reverse proxy server in http accelerator mode.

=back

If you are a newbie, I would recommend that you start with the first
option and work on getting your feet wet with apache and mod_perl.
Later, you can decide whether to move to the second one which allows
better tuning at the expense of more complicated administration, or to
the third option -- the more state-of-the-art-yet-suspiciously-new DSO
system, or to the fourth option which gives you even more power.

=over 8

=item 1

The first option will kill your production site if you serve a lot of
static data from large (4 to 15MB) webserver processes. On the other hand,
while testing you will have no other server interaction to mask or add
to your errors.

=item 2

This option allows you to tune the two servers individually, for
maximum performance.

However, you need to choose between running the two servers on
multiple ports, multiple IPs, etc., and you have the burden of
administering more than one server.  You have to deal with proxying or
fancy site design to keep the two servers in synchronization.

=item 3

With DSO, modules can be added and removed without recompiling the
server, and their code is even shared among multiple servers.

You can compile just once and yet have more than one binary, by using
different configuration files to load different sets of modules.  The
different Apache servers loaded in this way can run simultaneously to
give a setup such as described in the second option above.

On the down side, you are playing at the bleeding edge.

You are dealing with a new solution that has weak documentation and is
still subject to change.  It is still somewhat platform specific.
Your mileage may vary.

The DSO module (C<mod_so>) adds size and complexity to your binaries.

Refer to the section L<"Pros and Cons of Building mod_perl as
DSO|strategy/Pros_and_Cons_of_Building_mod_pe"> for more information.

Build details: L<Build mod_perl as DSO inside Apache source tree via
APACI|install/Build_mod_perl_as_a_DSO_inside_t>



=item 4

The fourth option (proxy in http accelerator mode), once correctly
configured and tuned, improves the performance of any of the above
three options by caching and buffering page results.

=back








=head1 Alternative architectures for running one and two servers

The next part of this chapter discusses the pros and the cons of each
of these presented configurations.  L<Real World Scenarios
Implementaion|scenario/> describes the implementation techniques of
these schemes.

We will look at the following installations:

=over

=item * Standalone mod_perl Enabled Apache Server

=item * One Plain Apache and One mod_perl-enabled Apache Servers

=item * One light non-Apache and One mod_perl enabled Apache Servers

=item * Adding a Proxy Server in http Accelerator Mode

=back


=head2 Standalone mod_perl Enabled Apache Server

The first approach is to implement a straightforward mod_perl
server. Just take your plain apache server and add mod_perl, like you
add any other apache module. You continue to run it at the port it was
running before. You probably want to try this before you proceed to
more sophisticated and complex techniques.

The advantages:

=over 4

=item *

Simplicity. You just follow the installation instructions, configure
it, restart the server and you are done.

=item *

No network changes.  You do not have to worry about using additional
ports as we will see later.

=item *

Speed. You get a very fast server, you see an enormous speedup from
the first moment you start to use it.

=back

The disadvantages:

=over 4

=item *

The process size of a mod_perl-enabled Apache server is huge (maybe
4Mb at startup and growing to 10Mb and more, depending on how you use
it) compared to the typical plain Apache. Of course if memory sharing
is in place, RAM requirements will be smaller.

You probably have a few tens of child processes.  The additional
memory requirements add up in direct relation to the number of child
processes. Your memory demands are growing by an order of magnitude,
but this is the price you pay for the additional performance boost of
mod_perl.  With memory prices so cheap nowadays, the additional cost
is low -- especially when you consider the dramatic performance boost
mod_perl gives to your services with every 100Mb of RAM you add.

While you will be happy to have these monster processes serving your
scripts with monster speed, you should be very worried about having
them serve static objects such as images and html files.  Each static
request served by a mod_perl-enabled server means another large
process running, competing for system resources such as memory and CPU
cycles.  The real overhead depends on static objects request rate.
Remember that if your mod_perl code produces HTML code which includes
images, each one will turn into another static object request. Having
another plain webserver to serve the static objects solves this
unpleasant obstacle. Having a proxy server as a front end, caching the
static objects and freeing the mod_perl processes from this burden is
another solution. We will discuss both below.

=item *

Another drawback of this approach is that when serving output to a
client with a slow connection, the huge mod_perl-enabled server
process (with all of its system resources) will be tied up until the
response is completely written to the client.  While it might take a
few milliseconds for your script to complete the request, there is a
chance it will be still busy for some number of seconds or even
minutes if the request is from a slow connection client. As in the
previous drawback, a proxy solution can solve this problem. More on
proxies later.

Proxying dynamic content is not going to help much if all the clients
are on a fast local net (for example, if you are administering an
Intranet.) On the contrary, it can decrease performance. Still,
remember that some of your Intranet users might work from home through
slow modem links.

=back

If you are new to mod_perl, this is probably the best way to get
yourself started.

And of course, if your site is serving only mod_perl scripts (close to
zero static objects, like images), this might be the perfect choice
for you!

For implementation notes see the "L<One Plain and One mod_perl enabled
Apache Servers|scenario/One_Plain_and_One_mod_perl_enabl>" section in
implementations chapter.

=head2 One Plain Apache and One mod_perl-enabled Apache Servers

As I have mentioned before, when running scripts under mod_perl, you
will notice that the httpd processes consume a huge amount of virtual
memory, from 5Mb to 15Mb and even more.  That is the price you pay for
the enormous speed improvements under mod_perl. (Again -- shared
memory keeps the real memory that is being used much smaller :)

Using these large processes to serve static objects like images and
html documents is overkill. A better approach is to run two servers: a
very light, plain apache server to serve static objects and a heavier
mod_perl-enabled apache server to serve requests for dynamic
(generated) objects (aka CGI).

From here on, I will refer to these two servers as B<httpd_docs>
(vanilla apache) and B<httpd_perl> (mod_perl enabled apache).

The advantages:

=over 4

=item *

The heavy mod_perl processes serve only dynamic requests, which allows
the deployment of fewer of these large servers.

=item *

C<MaxClients>, C<MaxRequestsPerChild> and related parameters can now
be optimally tuned for both C<httpd_docs> and C<httpd_perl> servers,
something we could not do before. This allows us to fine tune the
memory usage and get a better server performance.

Now we can run many lightweight C<httpd_docs> servers and just a few
heavy C<httpd_perl> servers.

=back

An B<important> note: When a user browses static pages and the base
URL in the B<Location> window points to the static server, for example
C<http://www.nowhere.com/index.html> -- all relative URLs (e.g. C<E<lt>A
HREF="/main/download.html"E<gt>>) are being served by the light plain
apache server. But this is not the case with dynamically generated
pages. For example when the base URL in the B<Location> window points
to the dynamic server --
(e.g. C<http://www.nowhere.com:8080/perl/index.pl>) all relative URLs
in the dynamically generated HTML will be served by the heavy mod_perl
processes. You must use fully qualified URLs and not relative ones!
C<http://www.nowhere.com/icons/arrow.gif> is a full URL, while
C</icons/arrow.gif> is a relative one. Using C<E<lt>BASE
HREF="http://www.nowhere.com/"E<gt>> in the generated HTML is another way
to handle this problem. Also the C<httpd_perl> server could rewrite
the requests back to C<httpd_docs> (much slower) and you still need
the attention of the heavy servers.  This is not an issue if you hide
the internal port implementations, so the client sees only one server
running on port C<80>. (See L<Publishing Port Numbers other than
80|config/Publishing_Port_Numbers_other_th>)


The disadvantages:

=over 4

=item *

An administration overhead.

=over 4

=item *

The need for two different sets of configuration, log and other files.
We need a special directory layout to manage these. While some
directories can be shared between the two servers (like the C<include>
directory, containing the apache include files -- assuming that both
are built from the same source distribution), most of them should be
separated and the configuration files updated to reflect the changes.


=item *

The need for two sets of controlling scripts (startup/shutdown) and
watchdogs.

=item *

If you are processing log files, now you probably will have to merge
the two separate log files into one before processing them.

=back

=item *

Just as in the one server approach, we still have the problem of a
mod_perl process spending its precious time serving slow clients, when
the processing portion of the request was completed a long time ago.
Deploying a proxy solves this, and will be covered in the next
section.

As with the single server approach, this is not a major disadvantage
if you are on a fast network (i.e. Intranet). It is likely that you do
not want a buffering server in this case.

=back

Before you go on with this solution you really want to look at the
L<Adding a Proxy Server in http Accelerator
Mode|strategy/Adding_a_Proxy_Server_in_http_Ac> section.

For implementation notes see the "L<One Plain and One mod_perl enabled
Apache Servers|scenario/One_Plain_and_One_mod_perl_enabl>" section in
implementations chapter.

=head2 One light non-Apache and One mod_perl enabled Apache Servers

If the only requirement from the light server is for it to serve
static objects, then you can get away with non-apache servers having
an even smaller memory footprint. C<thttpd> has been reported to be
about 5 times faster then apache (especially under a heavy load),
since it is very simple and uses almost no memory (260k) and does not
spawn child processes.

Meta: Hey, No personal experience here, only rumours. Please let me
know if I have missed some pros/cons here. Thanks!

The Advantages:

=over 4

=item *

All the advantages of the 2 servers scenario.

=item *

More memory saving. Apache is about 4 times bigger then B<thttpd>, if
you spawn 30 children you use about 30M of memory, while B<thttpd>
uses only 260k - 100 times less! You could use the 30M you've saved to
run a few more mod_perl servers.

The memory savings are significantly smaller if your OS supports
memory sharing with Dynamically Shared Objects (DSO) and you have
configured apache to use it.  If you do allow memory sharing, 30 light
apache servers ought to use only about 3 to 4Mb, because most of it
will be shared.  There is no memory sharing if apache modules are
statically compiled into httpd.

=item *

Reported to be about 5 times faster then plain apache serving static
objects.

=back


The Disadvantages:

=over 4

=item *

Lacks some of apache's features, like access control, error
redirection, customizable log file formats, and so on.

=back

META: It seems that khttpd, or Phhttpd should be even faster for
static content serving, add more info about these two!






=head1 Adding a Proxy Server in http Accelerator Mode

At the beginning there were 2 servers: one plain apache server, which
was I<very light>, and configured to serve static objects, the other
mod_perl enabled (I<very heavy>) and configured to serve mod_perl
scripts.  We named them C<httpd_docs> and C<httpd_perl> respectively.

The two servers coexist at the same IP address by listening to
different ports: C<httpd_docs> listens to port 80
(e.g. http://www.nowhere.com/images/test.gif) and C<httpd_perl>
listens to port 8080 (e.g. http://www.nowhere.com:8080/perl/test.pl).
Note that I did not write http://www.nowhere.com:80 for the first
example, since port 80 is the default port for the http service. Later
on, I will be changing the configuration of the C<httpd_docs> server
to make it listen to port 81.

Now I am going to convince you that you B<want> to use a proxy server
(in the http accelerator mode). The advantages are:

=over 8

=item *

Allow serving of static objects from the proxy's cache (objects that
previously were entirely served by the C<httpd_docs> server).

=item *

You get less I/O activity reading static objects from the disk (proxy
serves the most "popular" objects from RAM - of course you benefit
more if you allow the proxy server to consume more RAM). Since you do
not wait for the I/O to be completed you are able to serve static
objects much faster.

=item *

The proxy server acts as a sort of output buffer for the dynamic
content.  The mod_perl server sends the entire response to the proxy
and is then free to deal with other requests.  The proxy server is
responsible for sending the response to the browser.  So if the
transfer is over a slow link, the mod_perl server is not waiting
around for the data to move.

Using numbers is always more convincing :) Let's take a user connected
to your site with 28.8 kbps (bps == bits/sec) modem. It means that the
speed of the user's link is 28.8/8 = 3.6 kbytes/sec. I assume an
average generated HTML page to be of 40kb (kb == kilobytes) and an
average script that generates this output in 1 second. How long will
the server wait before the user gets the whole output response? A
simple calculation reveals pretty scary numbers -- it will have to
wait for another 12 secs (40kb/3.6kb), when it could serve another 11
(12/1-1) dynamic requests in this time.

This very simple example shows us that we need only one twelfth the
number of children running, which means that we will need only one
twelfth of the memory (not quite true because some parts of the code
are shared).

But you know that nowadays scripts often return pages which are blown
up with javascript code and similar, which can make them of 100kb size
and the download time will be of the order of... (This calculation is
left to you as an exercise :)

Many users like to open many browser windows and do many things at
once (download files and browse graphically I<heavy> sites). So the
speed of 3.6kb/sec we were assuming before, may often be 5-10 times
slower.

=item *

We are going to hide the details of the server's implementation. Users
will never see ports in the URLs (more on that topic later). You can
have a few boxes serving the requests, and only one serving as a front
end, which spreads the jobs between the servers in a way that you can
control.  You can actually shut down a server, without the user even
noticing, because the front end server will dispatch the jobs to other
servers. (This is called a Load Ballancing and it's a pretty big
issue, which will not be discussed in this document. For more
information see L<'High-Availability Linux
Project'|download/High_Availability_Linux_Project>)

=item *

For security reasons, using any httpd accelerator (or a proxy in httpd
accelerator mode) is essential because you do not let your internal
server get directly attacked by arbitrary packets from whomever.  The
httpd accelerator and internal server communicate in expected HTTP
requests. This allows for only your public "bastion" accelerating www
server to get hosed in a successful attack, while leaving your
internal data safe.

=back

The disadvantages are:

=over 8

=item *

Of course there are drawbacks. Luckily, these are not functionality
drawbacks, but they are more administration hassle. You have another
daemon to worry about, and while proxies are generally stable, you
have to make sure to prepare proper startup and shutdown scripts,
which are run at boot and reboot as appropriate. Also, you might want
to set up the crontab to run a watchdog script.

=item *

Proxy servers can be configured to be light or heavy, the admin must
decide what gives the highest performance for his application. A proxy
server like squid is light in the concept of having only one process
serving all requests. But it can appear pretty heavy when it loads
objects into memory for faster service.

=back

Have I succeeded in convincing you that you want a proxy server?

If you are on a local area network (LAN), then the big benefit of the
proxy buffering the output and feeding a slow client is gone.  You are
probably better off sticking with a straight mod_perl server in this
case.

=head1 Implementations of Proxy Servers

As of this writing, two proxy implementations are known to be widely
used with mod_perl - B<squid> proxy server and B<mod_proxy> which is a
part of the apache server.  Let's compare them.

=head2 The Squid Server

The Advantages:

=over 4

=item *

Caching of static objects.  These are served much faster, assuming
that your cache size is big enough to keep the most frequently
requested objects in the cache.

=item *

Buffering of dynamic content, by taking the burden of returning the
content generated by mod_perl servers to slow clients, thus freeing
mod_perl servers from waiting for the slow clients to download the
data. Freed servers immediately switch to serve other requests, thus
your number of required servers goes down dramatically.

=item *

Non-linear URL space / server setup. You can use Squid to play some
tricks with the URL space and/or domain based virtual server support.

=back

The Disadvantages:

=over 4

=item *

Proxying dynamic content is not going to help much if all the clients
are on a fast local net.  Also, a message on the squid mailing list
implied that squid only buffers in 16k chunks so it would not allow a
mod_perl to complete immediately if the output is larger.

=item *

Speed. Squid is not very fast today when compared with the plain file
based web servers available. Only if you are using a lot of dynamic
features such as mod_perl or similar is there a reason to use Squid,
and then only if the application and the server are designed with
caching in mind.

=item *

Memory usage. Squid uses quite a bit of memory.

META: more details?

=item *

HTTP protocol level. Squid is pretty much a C<HTTP/1.0> server, which
seriously limits the deployment of C<HTTP/1.1> features.

=item *

HTTP headers, dates and freshness. The squid server might give out
stale pages, confusing downstream/client caches.(You update some
documents on the site, but squid will still serve the old ones.)

=item *

Stability. Compared to plain web servers, Squid is not the most stable.

=back

The pros and cons presented above lead to the idea that you might want
to use squid for its dynamic content buffering features, but only if
your server serves mostly dynamic requests. So in this situation, when
performance is the goal, it is better to have a plain apache server
serving static objects, and squid proxying the mod_perl enabled server
only.

For implementation details see the sections L<Running One Webserver
and Squid in httpd Accelerator
Mode|scenario/Running_One_Webserver_and_Squid_> and the L<Running Two
Webservers and Squid in httpd Accelerator
Mode|scenario/Running_Two_webservers_and_Squid> in the implementations
chapter.


=head2 Apache's mod_proxy

I do not think the difference in speed between apache's B<mod_proxy>
and B<squid> is relevant for most sites, since the real value of what
they do is buffering for slow client connections.  However, squid runs
as a single process and probably consumes fewer system resources.

The trade-off is that mod_rewrite is easy to use if you want to spread
parts of the site across different back end servers, while mod_proxy
knows how to fix up redirects containing the back-end server's idea of
the location.  With squid you can run a redirector process to proxy to
more than one back end, but there is a problem in fixing redirects in
a way that keeps the client's view of both server names and port
numbers in all cases.

The difficult case is where:

=over

=item * You have DNS aliases that map to the same IP address and 

=item * You want the redirect to port 80 and

=item * The server is on a different port and

=item * You want to keep the specific name the browser has already
sent, so that it does not change in the client's B<Location> window.

=back



The Advantages:

=over 4

=item *

No additional server is needed. We keep the one plain plus one
mod_perl enabled apache servers. All you need is to enable
C<mod_proxy> in the C<httpd_docs> server and add a few lines to
C<httpd.conf> file.

=item *

The C<ProxyPass> and C<ProxyPassReverse> directives allow you to hide
the internal redirects, so if C<http://nowhere.com/modperl/> is
actually C<http://localhost:81/modperl/>, it will be absolutely
transparent to the user. C<ProxyPass> redirects the request to the
mod_perl server, and when it gets the response, C<ProxyPassReverse>
rewrites the URL back to the original one, e.g:

  ProxyPass        /modperl/ http://localhost:81/modperl/
  ProxyPassReverse /modperl/ http://localhost:81/modperl/

=item *

It does mod_perl output buffering like squid does. See the L<Using
mod_proxy|scenario/mod_proxy> notes for more details.

=item *

It even does caching. You have to produce correct C<Content-Length>,
C<Last-Modified> and C<Expires> http headers for it to work. If some
of your dynamic content does not change frequently, you can
dramatically increase performance by caching it with C<ProxyPass>.

=item *

C<ProxyPass> happens before the authentication phase, so you do not
have to worry about authenticating twice.

=item *

Apache is able to accelerate secure HTTP requests completely, while also
doing accelerated HTTP.  With Squid you have to use an external redirection
program for that.

=item *

The latest (apache 1.3.6 and later) Apache proxy accelerated mode is
reported to be very stable.

=back


The Disadvantages:

=over 4

=item *


Users have reported that it might be a bit slow, but the latest version is
fast enough. 

(META: How fast is enough? :) Any figures here? 

=back

For implementation see the "L<Using
mod_proxy|scenario/mod_proxy>" section in the 
implementation chapter.



=head1 When One Machine is not Enough for SQL DB and mod_perl

You have begun your business as a small service providing web-site.
After a while your business becomes very popular and at some point you
understand that it has outgrown the capacity of your machine and you
have moved everything onto a stronger machine with more memory, a
stronger CPU and a faster hard disk.

The situation comes back to normal but not for a long, as a demand for
your services keeps on growing and just a little time after you've
upgraded your machine, it cannot stand the load again. Should you buy
an even stronger and very expensive machine or start looking for
another solution?

A typical web service consists of two main software components, the
database server and the web server.

A typical user-server interaction consists of accepting the query
parameters filled into an HTML form and submitted to the web server by
a user, converting these parameters into a database query, sending it
to the database server, accepting the results of the executed query,
formatting them into a nice HTML page, and sending it to a user's
Internet browser or another application that created the request.

This figure depicts the above description:

             1                      2
  [      ] ====> [               ] ====> [                 ]
  [ User ]       [ Apache Server ]       [ Database Server ]
  [      ] <==== [               ] <==== [                 ]
             4                       3

This schema is known as a 3-tier architecture in the computing world.

3-tier architecture means splitting up several processes of your
computing solution between different machines.

1st you have the client, who will see the data on its screen and can
give instruction to modify or process the data.  In our case, an
Internet browser.

2nd you have the application server, which does the actual processing
of the data and sends it back to the client.  In our case, a mod_perl
enabled apache server.

3rd you have the database server, which stores and retrieves all the
data for the application server.

+We are interested only in the 2nd and the 3rd tiers; we don't specify
user machine requirements, since mod_perl is all about server side
programming. The only thing the user should be able to do is to render
the generated HTML from the response, which any simple browser will
do. Of course I'm not talking about the case where you return some
heavy Java applets, but that movie is screened in another theater.



=head2 Servers' Requirements

Let's first understand what kind of software the web and database
servers are, what do they need to run fast and what implications they
have on the rest of the system software.

The three important machine components are the hard disk, the amount
of RAM and the CPU type.

In the average case the mod_perl server is mostly RAM hungry, while
SQL database server needs a very fast hard-disk the most.  Of course
if your mod_perl process reads a lot from the disk (which is a quite
infrequent phenomenon) you will need a fast disk too. And if your
database server has to do a lot of sorting of the big tables and do
lots of big table joins, you will need a lot of RAM too.

If we would specify average "virtual" requirements for each machine,
that's what we'd get:

An I<"ideal"> mod_perl machine:

  * HD:  low-end (no real IO, mostly logging)
  * RAM: the more the better
  * CPU: medium-high (according to needs)

An I<"ideal"> database server machine:

  * HD:  high-end
  * RAM: big amount   (big joins, sorting of many records)
         small amount (otherwise)
  * CPU: medium-high (according to needs)


=head2 The Problem

With the database and the httpd on the same machine, you have
conflicting interests.

During the peak load, Apache will spawn more processes and use RAM
that the database server might have been using, or that the kernel was
using on its behalf in the form of cache.  You will starve your
database of resources at the time when it needs those resources the
most.

Disk I/O contention is the biggest time issue.  Adding another disk
wouldn't cut I/O times because the database is the only one who does
I/O - since mod_perl processes has all the code loaded in memory. (I'm
talking about code that does pure perl and SQL processing) so it's
clear that the DB is I/O and CPU bounded (RAM only if there are big
joins to make) and mod_perl CPU and mostly RAM bounded.

The problem exists, but it doesn't mean that you cannot run the
application and the web servers on the same machine.  There is a very
high degree of parallelism in modern PC architecture.  The I/O
hardware is helpful here.  The machine can do many things while a SCSI
subsystem is processing a command, or the network hardware is writing
a buffer over the wire.

If a process is not runnable (that is, it is blocked waiting for I/O
or similar), it is not using significant CPU time.  The only CPU time
that will be required to maintain a blocked process is the time it
takes for the operating system's scheduler to look at the process,
decide that it is still not runnable, and move on to the next process
in the list.  This is hardly any time at all.  If there are two
processes and one of them is blocked on I/O and the other is CPU
bound, the blocked process is getting 0% CPU time, the runnable
process is getting 99.9% CPU time, and the kernel scheduler is using
the remainder.


=head2 The Solution

Adding another machine, which allows a set-up where both the database
and the web servers run on its dedicated machine.

=head3 Pros

=over

=item * Hardware Requirements

That allows you to scale two requirements independently.

If your httpd processes are heavily weighted with respect to RAM
consumption, you can easily add another machine to accommodate more
httpd processes, without changing your database machine.

If your database is CPU intensive, but your httpd doesn't need much
CPU time, you can get low end machines for the httpd and a high end
machine with a very fast CPU for the database server.

=item * Scalability

Since your web server is not depending on the database server location
any more, you can add more web servers hitting the same database
server, using the existing infrastructure.

=item * Database Security

Once you have multiple web server boxes the backend database becomes a
single point of failure so it's a good idea to shield it from direct
internet access, something you couldn't do when you had both servers
on the same machine.

=back



=head3 Cons

=over

=item * Network latency

When the request to the database server like mysql are made at the
same machine the database server is running on, it uses the UNIX
sockets compared to the TCP/IP socket when the client submits the
query from another machine.  UNIX sockets are very fast since there is
no network delays, since all the communications happens within the
same box.  TCP/IP sockets communication totally depends on the quality
and the speed of the network the two machines are connected with.

Basically you can have almost the same client-server speed if you
install a very fast and dedicated network between the two machines. It
might impose a cost of additional NICs but it's probably insignificant
compared to the speed up you gain.

But even the normal network that you have would probably fit as well,
because the networks delays are probably much smaller than the time it
takes to execute the query. In contrast to the previous paragraph, you
really want to test the added overhead, since the network can be quite
slow especially at the peak hours.

How do you know what overhead is a significant one? All you have to
measure is the average time spent in the web server and the database
server. If any of the two numbers is at least 20 times bigger than the
added overhead of the network you are all set.

To give you some numbers -- if your query takes about 20 milliseconds
to process and only 1 millisecond to deliver the results, it's
good. If the delivery takes about half of the time the processing
takes you should start thinking to switch to a faster and/or dedicated
network.

The implications of the slow network can be quite bad. If the network
is slow mod_perl processes remain open waiting for data from the
database server and eats even more RAM as new child processes pop up
to handle new requests. So the overall machine performance can be
worse than it was originally when you have had a single machine for
both servers.


=back


=head2 Three Machines Model

Since we are talking about giving a dedicated machine for each server,
you might consider adding the third machine to do the proxy work, this
will make your setup even more flexible since it will enable you to
proxy-pass all request to more than one mod_perl running box, but many
of them. It will enable you doing a load balancing if and when you
will need that.

Generally the proxy machine can be very light when they serve just a
little traffic and mainly proxy-pass to the mod_perl processes. Of
course you can put this machine to serve the static content and then
the hardware requirement will depend on the number of object you will
have to serve and the hit rate.





=head1 Do Not Run Everything on One mod_perl Server

Let's assume that you have two different sets of scripts/code which
have a little or nothing in common at all (different modules, no code
sharing). Typical numbers can be four megabytes of unshared and four
megabytes of shared memory for each code set, plus three megabytes of
shared basic mod_perl stuff.  Which makes each process 17Mb in size
when the two code sets are loaded. (3Mb (server) + 4Mb (shared 1st
code set ) + 4Mb (unshared 1st code set ) + 4Mb (shared 2nd code set )
+ 4Mb (unshared 2nd code set ). Where eleven megabytes are shared and
eight megabytes not.

We assume that four megabytes is the size of each code set unshared
memory. This is pretty typical size of unshared memory, especially
when connecting to databases, as the database connections cannot be
shared, and especially DB's like Oracle take lots of RAM per
connection.

Let's assume that we have 260 megabytes of RAM dedicated for the
webserver.

According to the equation developed in the section: "L<Choosing
MaxClients|performance/Choosing_MaxClients>":

                    Total_RAM - Max_Process_Size
  MaxClients = ---------------------------------------
               Max_Process_Size - Shared_RAM_per_Child


  MaxClients = (260 - 17)/(17-11) = 40

We see that we can run 40 processes, using the given memory and the
two code sets in the same server.

Now consider this practical decision. Since we have recognized that
the code sets are very distinct in nature and there is no significant
memory sharing in place, the wise thing to do is to split the two code
sets between two mod_perl servers (a single mod_perl server actually
is a set of the parent process and a number of the child
processes). So instead of running everything on one server, now we
move the second code set onto another mod_perl server. At this point
we are talking about a single machine.

Let's look at the figures again. After the split we will have 20
servers of eleven megabytes (4Mb unshared + 7mb shared) and another 20
servers of eleven megabytes.

How much memory do we need now? From the above equation we derive:

  Total_RAM = MaxClients * (Max_Process_Size - Shared_RAM_per_Child)
              + Max_Process_Size

And using the numbers:

  Total_RAM = 2 * (20 * (11-7) + 11) = 182

A total of 182 megabytes of memory required. But, hey, we have 260Mb
of memory. We've got 78Mb of memory freed up. If we recalculate again
the C<MaxClients> we will see that we can run almost 60 servers:

  MaxClients = (260 - 11*2)/(11-8) = 60

So we can run about 20 more servers using the same memory size. 30
servers for each code set. We have enlarged the servers pool by a half
without changing machine's hardware.

Moreover this new setup allows us to fine tune the two code sets,
since in reality the smaller in size code base might have a higher
hit rate, so we can benefit even more. 

Let's assume that based on the usage statistics we know that the first
code set is deployed in 70% of requests and the other 30% are used by
the second set. Now we assume that the first code set requires only
5Mbytes of RAM (3Mb shared plus 2Mb unshared) over the basic mod_perl
server size, and the second set needs 11Mbytes (7Mb shared and 4Mb
unshared).

Lets compare this new requirement with our original 50%/50% setup.

So now the first mod_perl server running the first code set will have
all its processes of 8Mb (3Mb (server shared) + 3Mb (code shared) +
2Mb (code unshared), and the second 14Mb (3+7+4).  Given that we have
a 70:30 hits relation and that we have 260Mbytes of available memory,
we have to solve these two equations:

  X/Y = 7/3

  X*(8-6) + 8 + Y*(14-10) + 14 = 260

where X is the total number of the processes the first code set can
use and Y the second. The first equation reflect the 70:30 hits
relation, and the second uses the equation for the total memory
requirements for the given number of servers and the shared and
unshared memory sizes.

When we solve these equations, we get that X equals 63 and Y equals
27. So we have a total of 90 servers -- we have twice and a half more
servers running compared to the original setup using the same memory
size

The hits rate optimized solution and the fact that the code sets can
be different in their memory requirements, allowed us to run 30 more
servers in total and gave us 33 more servers (63 versus 30) for the
most wanted code base, relative to the simple 50:50 split as in the
first example.

Of course if you can identify more than two distinct sets of code and
your hits rate statistics may require more complicated decisions.  You
ought to make even more splits and run three and more mod_perl
servers.

Remember that having too many running processes doesn't necessarily
mean a better performance because of all of them will fight over CPU
time slices. The more processes are running the less CPU time each
gets the slower the overall performance will be. Therefore after
hitting a certain load you might want to start spreading servers over
different machine.

In addition to the obvious memory saving you gain the power to
troubleshoot problems, that occur, much easier, when you have
different components running on different servers. It's quite possible
that a little change in the server configuration coming to fix or
improve something in one code set, might completely break the second
code set. For example if you upgrade the first code set and it
requires an update of some modules that both code bases rely on. But
there is a chance that the second code set won't work with a new
module it was relying on.



=head1 Do Not Put mod_ssl into mod_perl Server

If you need an SSL functionality, you can get it by adding the mod_ssl
or equivalent apache_ssl to the light front-end server (httpd_docs) or
the heavy back-end mod_perl server (httpd_perl). ( The configuration
and installation instructions are located
L<here|install/mod_perl_and_mod_ssl_openssl_>.)

The question is whether it's a good idea to add mod_ssl into the
back-end mod_perl enabled server.  Given that your internal network is
secured or if both the front and back end servers are running on the
same machine and you can ensure a safe communication between the
processes there is no need for an encrypted traffic between them.

If this is the situation you don't have to put mod_ssl into the
already too much heavy mod_perl server. You will have the external
traffic encrypted by the front-end server, which will proxy-pass the
unencrypted request and response data internally.

Another important point is if you put the mod_ssl on the back-end, you
have to tunnel back your images to it (i.e. have the back-end serve
the images) defeating the whole purpose of having the front-end
lightweight server. 

You cannot serve a secure page which includes non-secured
information. If you fetch an html over SSL and have an
C<E<lt>IMGE<gt>> tag that fetches the image from the non-secure
server, the image show broken. This is true for any other non-secured
object as well. Of course if the generated response doesn't include
any embedded objects, like images -- this is not a problem.

Choosing the front-end machine to have an SSL functionality also
simplifies configuration of mod_perl by eliminating VirtualHost
duplication for SSL. mod_perl configuration files can be plenty
difficult without the mod_ssl overhead.

Also assuming that you have front-end machines under-worked anyway,
especially if you run a high-volume web service deploying a cluster of
machines to serve requests, you save some CPU as it's known that SSL
connections are about 100 times more CPU intensive than non-SSL
connections.

Of course caching session keys so you don't have to set up a new
symmetric key for every single connection, improves the situation.  If
you use the shared memory session caching mechanism that mod_ssl
supports, then the overhead is actually rather small except for the
initial connection.

But then on the other hand, why even bother to run a full scale
mod_ssl in front? You might as well just choose a small tunnel/port
forwarding application like Stunnel or one of the many other mentioned
at http://www.openssl.org/related/apps.html .

Of course if you do a heavy SSL processing you should really be
offloading it to dedicated cryptography hardware.








=head1 Pros and Cons of Building mod_perl as DSO

On modern Unix derivatives there exists a nifty mechanism usually
called dynamic linking/loading of Dynamic Shared Objects (DSO) which
provides a way to build a piece of program code in a special format
for loading it at run-time into the address space of an executable
program.

As of Apache 1.3, the configuration system supports two optional
features for taking advantage of the modular DSO approach: compilation
of the Apache core program into a DSO library for shared usage and
compilation of the Apache modules into DSO files for explicit loading
at run-time.

Should you use this method? Read the pros and cons and decide for
yourself.

Pros:

=over 

=item *

The server package is more flexible at run-time because the actual
server process can be assembled at run-time via C<LoadModule>
I<httpd.conf> configuration commands instead of I<Configuration>
C<AddModule> commands at build-time.  For instance this way one is
able to run different server instances (standard & SSL version, with
and without mod_perl) with only one Apache installation.

=item *

The server package can be easily extended with third-party modules
even after installation.  This is at least a great benefit for vendor
package maintainers who can create a Apache core package and
additional packages containing extensions like PHP3, mod_perl,
mod_fastcgi, etc.

=item *

Easier Apache module prototyping because with the DSO/apxs pair you
can both work outside the Apache source tree and only need an S<apxs
-i> command followed by an S<apachectl restart> to bring a new version
of your currently developed module into the running Apache server.


=back

Cons:

=over 

=item *

The DSO mechanism cannot be used on every platform because not all
operating systems support dynamic loading of code into the address
space of a program.

=item *

The server is approximately 20% slower at startup time because of the
symbol resolving overhead the Unix loader now has to do.

=item *

The server is approximately 5% slower at execution time under some
platforms because position independent code (PIC) sometimes needs
complicated assembler tricks for relative addressing which are not
necessarily as fast as absolute addressing.

=item *

Because DSO modules cannot be linked against other DSO-based libraries
(ld -lfoo) on all platforms (for instance a.out-based platforms
usually don't provide this functionality while ELF-based platforms do)
you cannot use the DSO mechanism for all types of modules. Or in other
words, modules compiled as DSO files are restricted to only use
symbols from the Apache core, from the C library (libc) and all other
dynamic or static libraries used by the Apache core, or from static
library archives (libfoo.a) containing position independent code. The
only chances to use other code is to either make sure the Apache core
itself already contains a reference to it, loading the code yourself
via dlopen() or enabling the SHARED_CHAIN rule while building Apache
when your platform supports linking DSO files against DSO libraries.

=item *

Under some platforms (many SVR4 systems) there is no way to force the
linker to export all global symbols for use in DSO's when linking the
Apache httpd executable program. But without the visibility of the
Apache core symbols no standard Apache module could be used as a
DSO. The only chance here is to use the SHARED_CORE feature because
this way the global symbols are forced to be exported. As a
consequence the Apache src/Configure script automatically enforces
SHARED_CORE on these platforms when DSO features are used in the
Configuration file or on the configure command line.

=back





=cut





