Assuming the request resolves some dynamic content, once
the http.sys has queued the request, a worker process picks it
up and executes the actual user code to process the request,
and then generates the response back to the user. These worker
processes live in application pools that completely isolate
one group of applications from another. An application pool is
simply one or more processes that service a given work queue.
You can place multiple applications in a single application
pool or you can limit your application pools to a single
application. What's more, you can define the number of
processes that service an application pool. So, you can have a
single process service multiple applications housed in a
single pool, or you can have multiple processes service a
single application in a pool.
This last example, in which you have multiple processes
serving a single application, is called a Web garden.
Conceptually, it is the same as a Web farm. However, instead
of spanning several machines, your application spans several
processes. In Web gardens, http.sys distributes requests
across each process similar to the way a load balancer spreads
requests across different machines in a Web farm. Besides the
obvious benefit of increased reliability-should one process
fail, other processes in the garden will continue to respond
to the load-there are a couple of more subtle reasons you
should consider this approach.
First, this approach will help you work around one of the
more vexing problems of server development (and Web
development in particular): hot locks. Oftentimes when I
review a poorly performing Web app, it turns out that there is
a problem because either one tiny section of the code is
poorly written or the inherent business logic has some locking
issues. These issues may result from a string concatenation in
a tight loop (the poorly written example) or from a long
running calculation (the business requirement example). Either
way, the end result is the same: when the server is under
heavy load, locks in this section quickly start to back up,
and then blocked threads start to cascade through the rest of
the application. While implementing a Web garden does not
resolve this issue, it will distribute these locks over a
number of processes.
The second benefit is that deploying your apps in a Web
garden from the outset forces you to deal with all the issues
(such as session management and security) that you will need
to address later on when you move to a Web farm. This way, you
can plan for these issues in your core architecture so that
you will not have to change your code (undertake an additional
dev/test/release cycle) when you want to scale it out.
So far I have only discussed isolation in terms of
preventing one application from crashing and taking down
another application with it. You also need to account for the
possibility that a poorly behaved application will tie up all
the server resources and effectively deny the server to the
other applications on the box. While this may not seem like a
big deal to people who dedicate a Web server to a specific
task or application, it is a huge deal for hosting
companies.
IIS 6.0 addresses this in two ways. First, as you will see
later in the Reliability section, IIS 6.0 automatically
restarts applications that exceed certain thresholds such as
memory usage. The second route is through the use of several
quotas that IIS makes available to administrators. Using these
quotas, you can limit the CPU cycles and bandwidth available
to a site. If your site permits uploads, you can limit the
disk space taken through the use of NTFS quotas, which limit
the total space used by the identity under which the process
runs, or you can limit the amount of data that can be uploaded
at one time. Besides ensuring that a single application does
not use up all the available disk space, this latter approach
is also great for preventing malicious hackers from uploading
trojans.
Should an application fail, the worst it can do is crash
the worker process. While this is better than bringing down
the entire server, it still leaves you in a bad spot because
your application may be down. Should this happen, http.sys
continues to process inbound requests and places them on the
queue for processing. When the WWW service detects a failed
worker process, it automatically starts up a new one to
process any requests on the work queue.
So how does IIS know when a process has failed? When it
starts up a new process, IIS opens a special communication
channel to it. Should this process fail, it will drop this
communication channel. When the channel goes dead, IIS
immediately detects this and can take whatever action you
specify, which is typically to restart the application.
Alternatively, if it constantly fails, you can take the
application pool out of service. Better yet, the response to a
failed process is extensible, so you can hook your own code
into the failure event. Using this approach, you could send a
message to the load balancer to take the failed server out of
the cluster.
This approach for dealing with failed applications works
fine if the process actually crashes. However, if you have
spent much time debugging ASP or ISAPI apps, you know that it
is much more common for a poorly written application to hang
or otherwise become non-responsive. Fortunately, IIS can
address these cases also.
In addition to the communication channel, IIS performs
"looks alive" testing (essentially a ping) periodically on
each process. If a process fails to respond, typically IIS
kills the process and starts a new one. However, you can
configure it to start up a new process to handle new requests
while leaving the old process running in case it was blocked
performing some useful work (such as a long-running query for
your boss)-thus enabling the process to complete its task.
Rather than killing the task outright, you can configure IIS
to attach a debugger to this orphaned process in addition to
starting up a new process to handle incoming requests.
This is a huge boon to developers because it is often much
easier to get to the bottom of a problem by debugging a live
system than it is by diving into a dump file. The problem with
debugging a live system is that it conflicts with our other
need to keep systems up and running because usually no one
else can access the system until we finish our debug session.
The new work process model in IIS 6.0 fixes this because IIS
simply spins up another process to handle the new requests and
leaves the dead process in its failed state until we are
finished debugging it. If you do decide to turn the setting
on, be really careful because if an application fails many
times, you will have many, many orphaned processes sitting
around, and you will soon run out of memory.
Besides sitting around and waiting for an application to
fail, IIS can proactively monitor an application and recycle
after a set period of time or after certain thresholds are
reached. Using this approach, you can recycle your application
should the amount of physical or virtual memory grow above a
preset level. This is a great approach for dealing with memory
leaks in legacy code that you have inherited. Oftentimes, the
cost and effort required to track down and resolve these leaks
far exceeds the value of the application itself, so rather
than fixing the problem it is often preferable to simply
configure the hosting environment in such a way that will
enable the application to limp along until you can rewrite it.
You can also instruct IIS to recycle an application after a
fixed number of requests or after a certain amount of elapsed
time. Finally, if you detect an inconsistent state in your own
code, you can also programmatically set a process to
unhealthy, which will cause IIS to recycle it.
|