<<Prev
The IIS 6.0 Architecture and Process Model
The architecture
for Windows Server 2003 and IIS 6.0 is divided into two basic parts—a listener
process and a set of worker processes. The IIS 6.0 listener process is
implemented in the w3svc service, which is a long-running Windows NT service
activated by the Windows Service Control Manager (SCM). The listener process
waits for messages to arrive via HTTP and then dispatches them to the
appropriate worker process (w3wp.exe), which hosts the application code that
will finally process the request.
When a request
arrives on the network, it is processed by the kernel-mode HTTP stack
(http.sys) and delivered to the listener process. The w3svc process then looks
at the request URI and uses it to map the request to a specific IIS application
living inside a particular IIS application pool that is, in turn, hosted inside
an instance of the worker process (w3wp), as you see in Figure 1. The mapping
between the request URI and the application is based on configuration
information stored in the IIS metabase, which can be found in metabase.xml and
mbschema.xml, respectively, both of which are stored in the
%windir%\system32\inetsrv folder. Once w3svc has determined the destination
application, it can look it up in some internal data structures to resolve the
destination worker process and application pool.
Figure
1 IIS 6.0 Basic Architecture
Depending on the
state of the targeted IIS server, one of two things can happen at this point.
If the worker process and application pool are already running because a
previous request has been received for that pool, then no activation is
necessary. Therefore, the request is simply dispatched to the waiting worker
process. If no worker process exists to handle the current request, the
listener process must create a new instance of w3wp before dispatching the
request.
The IIS 6.0
worker process is a lightweight executable. When it starts up in response to an
activation request, the first thing it does is load a simple unmanaged shim DLL
(w3wphost.dll) that enables w3svc to communicate with the worker process. This
shim is also responsible for loading aspnet_isapi.dll, which implements the
interface between the managed components of ASP.NET and the unmanaged
components of IIS.
Aspnet_isapi.dll
is responsible for loading the common language runtime (CLR) into the worker
process and creating a default application domain, which is where the managed
hosting components of ASP.NET will live. These managed hosting components are
responsible for creating additional application domains (one per IIS
application) on demand and routing requests to them based on the request's URL.
IIS 7.0 and WAS
In IIS 6.0, the
w3svc service really did double duty. It acted as the HTTP listener because it
registered with http.sys and was the direct recipient of incoming HTTP traffic.
It also owned the process activation components responsible for starting new instances
of w3wp and dispatching requests appropriately. In IIS 7.0, these two
responsibilities have been refactored into separate Windows NT services. The
w3svc process retains its role as the HTTP listener, but the components
responsible for configuration and process activation have been factored into
WAS, which has three parts: the configuration manager, the process manager, and
the unmanaged listener adapter interface.
The configuration
manager reads application and application pool configuration from
applicationhost.config (the IIS 7.0 replacement for the metabase). The process
manager maps application pools to existing worker processes and is the one
responsible for spawning new instances of w3wp to host new application pools in
response to activation requests. The unmanaged listener adapter interface
defines how external listeners communicate activation requests they receive to
WAS.
The w3svc service
owns the communication with kernel-level http.sys and communicates HTTP
activation requests to WAS across the listener adapter interface.
WCF uses the
listener adapter interface to communicate activation requests that are received
over the supported non-HTTP protocols (namely TCP, Named Pipes, and MSMQ). The
WCF plumbing that actually receives requests over non-HTTP protocols is hosted
inside of SMSvcHost.exe, which hosts the following four long-running Windows NT
services: NetTcpPortSharing, NetTcpActivator, NetPipeActivator, and
NetMsmqActivator (see Figure 2).
Figure
2 Basic Architecture of IIS 7.0 and WAS
NetTcpPortSharing
is the WCF TCP port sharing service. It implements a centralized TCP listener
so that multiple processes can listen on the same TCP port. This service is
available even if IIS 7.0 is not installed.
NetTcpActivator
is the WCF TCP Activation Service. It communicates TCP activation requests to
WAS.
NetPipeActivator
is the WCF-named pipe activation service which communicates named pipe
activation requests to WAS.
NetMsmqActivator
is the WCF MSMQ activation service; it communicates MSMQ activation requests to
WAS.
Although these
services all live in the same binary, they are separate Windows NT services and
can be stopped and started individually to reduce both attack surface and
overhead. They are all examples of listener services and all behave in a
similar manner. For that reason, we will focus on TCP activation, which can
also serve as an example for named pipes and MSMQ. The only difference is the
particular type of network resource in use in each case.
Configuration and Multi-Protocol Addressing
In order for an
application to be activated by WAS, it first needs to be configured. Each
application hosted in WAS has a corresponding configuration entry located in
%windir%\system32\inetsrv\config\applicationhost.config. This information
includes items like the application pool that will host the application and a
URL fragment that uniquely identifies it.
Much like IIS
6.0, each application is associated with both an application pool and a site.
Each site has a set of address bindings for the network protocols it supports.
For example, an administrator might configure the default site to bind to HTTP
on port 80 and TCP on port 7777. This site might host two applications
(listening on /Foo and /Bar respectively) that live in separate application
pools. In this configuration, the /Foo application would listen on http://myserver.com/Foo as well as
net.tcp://myserver.com:7777/Foo, while /Bar would listen on http://myserver.com/Bar and
net.tcp://myserver.com:7777/Bar. Regardless of how they were received—since
each application pool gets its own worker process—all requests for /Foo would
go to worker process 1, while everything destined for /Bar would go to worker
process 2.
In general, an application's site governs the
set of network addresses associated with the application while its application
pool governs the worker process instance that will host it. Note that the one
worker process per application pool rule only holds true if you're not using
Web gardens. In the Web garden case, each application pool maps to its own set
of worker processes, up to the maximum number of worker processes specified in
the application pool's configuration.
How Listeners Know to Listen
A listener needs
to receive messages. For this, it needs to open a socket (or a pipe handle, or
start an MSMQ read, and so on). However, in order to receive the proper
messages, it needs to obtain the necessary addressing information from WAS.
This is accomplished during listener startup. The protocol's listener adapter
calls a function on the WAS listener adapter interface and essentially says,
"I am now listening on the net.tcp protocol; please use this set of
callback functions I'm handing you to tell me what I need to know." In
response, WAS will call back with any configuration it has for applications
that are set up to accept messages over the protocol in question. For the
example above, the TCP listener would be informed that there were two
applications (*:7777/Foo and *:7777/Bar) configured to use TCP. WAS also
assigns to each application a unique listener channel ID used for associating
requests with their destination applications.
The listener
process uses the configuration information provided by WAS to build up a
routing table, which it will use to map incoming requests to listener channel
IDs as they arrive. The mechanics of this mapping is an implementation detail
of the underlying protocol the listener is supporting. The important thing is
that each listener service must be able to look at an incoming message, say,
"Ah—this is destined for listener channel x," and dispatch the
request to WAS accordingly. It happens that WCF uses URIs to indicate the
destination of messages that arrive over TCP/MSMQ/named pipes, but other
protocols could conceivably implement this mapping in whatever way is
appropriate for them.
Once the listener
service has connected to WAS and received configuration information, it can
open its network resource and begin listening for messages. For TCP, this
causes NetTcpActivator to trigger a socket to open and an asynchronous call to
Socket.Accept to be made, at which point the listener essentially goes to sleep
until a message arrives.
Message-Based Activation over Non-HTTP Protocols
The listener
process wakes up and starts reading bytes when they arrive at the waiting
socket. At this point, the goal is to read just enough information to determine
the eventual destination of the forthcoming message and then pause while the
listener calls back to WAS and spawns the worker process. For TCP, the
destination address is determined by reading information out of the framing
protocol that surrounds the SOAP message. Once the listener has the destination
URI, it uses that URI as an index into its internal routing table and resolves
the URI into the corresponding listener channel ID assigned to that address by
WAS.
The listener
service then asks WAS to activate a worker process to handle the pending
request by calling the unmanaged WebhostOpenListenerChannelInstance method on
the listener adapter interface. In addition to other parameters,
WebhostOpenListenerChannelInstance takes a listener channel ID as well as a
blob of data (represented as a byte array) that will be used to initialize the
receiving side of the plumbing in the activated process. This blob is of no
interest to WAS as it's merely a convenience that allows each protocol listener
implementation to initialize its newly activated components.
When WAS receives
the call to WebhostOpenListenerChannelInstance, the process manager part of WAS
spawns a new instance of the worker process. The worker process then needs to
do some initialization itself before it can process messages received by the listener.
During this time the listener may continue to receive data from the network;
however it cannot dispatch the message to the worker until it receives
notification that worker process initialization is complete.
Worker Process Initialization
There
are several important components that are active inside the worker process, as
you can see in Figure 3. The architecture of a worker process is shown in
Figure 4.
Figure
3 Important Components in the Worker Process
Component
|
Description
|
Process Host
|
Loads the CLR
into the worker process and initializes the default application domain.
|
Application
Manager
|
Creates unique
application domains in response to application-level activation requests and
manages their lifetime.
|
Process
Protocol Handlers
|
Implement
protocol-specific process initialization logic.
|
Application
Domain Protocol Handlers
|
Reside in the
activated application domain and perform protocol-specific application domain
initialization.
|
Figure
4 Worker Process Initialization Architecture
Since IIS does
not implement any managed code components, the job of loading the CLR into the
worker process falls to the ASP.NET process host. WAS creates a process host
inside the worker process by calling an API exposed by aspnet_isapi.dll. WAS
passes this function a callback interface that the managed process host can use
for subsequent communication with WAS. The newly created process host is then
returned to WAS, allowing for two-way communication between WAS and the worker
process.
WAS uses the
instance of the process host it obtained from the worker process to start the
protocol-specific initialization routines. The activation system
StartProcessProtocolListenerChannel on the process host, passing it a protocol
key (such as "net.tcp") and a callback interface that the
protocol-specific handlers can use to talk back to WAS.
When the process
host receives a request to start a new process protocol listener channel inside
the worker process, the first thing it does is look up the protocol key in the
configuration. Users of WAS are required to register this prior to starting their
listeners. For example, WCF registers both a process protocol handler and an
application domain protocol handler for net.tcp. The configuration data shown
below can be found in the master web.config file in
%windir%\Microsoft.NET\Framework\v2.0.50727\CONFIG.
Configuration for net.tcp Protocol Handlers
<system.web>
<protocols>
<add name="net.tcp"
processHandlerType=
"System.ServiceModel.WasHosting.TcpProcessProtocolHandler,
System.ServiceModel.WasHosting, Version=3.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
appDomainHandlerType=
"System.ServiceModel.WasHosting.TcpAppDomainProtocolHandler,
System.ServiceModel.WasHosting, Version=3.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" validate="false" />
</protocols>
</system.web>
<protocols>
<add name="net.tcp"
processHandlerType=
"System.ServiceModel.WasHosting.TcpProcessProtocolHandler,
System.ServiceModel.WasHosting, Version=3.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089"
appDomainHandlerType=
"System.ServiceModel.WasHosting.TcpAppDomainProtocolHandler,
System.ServiceModel.WasHosting, Version=3.0.0.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" validate="false" />
</protocols>
</system.web>
Once the process
host has resolved the protocol key in configuration, it creates a new instance
of the configured process protocol handler type in the default application
domain and calls StartListenerChannel on it. This method takes two separate
callback interfaces. One allows communication with WAS (it is, in fact, the
same WAS callback object as passed to the process host). The other one allows
communication with the process host directly (see Figure 5).
Figure 5 Registering Protocol Handlers
A process
protocol handler (PPH) has a large degree of control over the hosting model for
a particular protocol. For example, the PPH could simply stop at this point in
the initialization routine and handle all subsequent requests in the default
application domain. However, WCF has chosen to retain the IIS 6.0 application
pool model, which activates each application in its own application domain so
that each one can be independently monitored and recycled. As such, the WCF
PPHs need to collaborate with the ASP.NET application manager, which governs
applications and application domain lifetime.
The process host
provides the PPH with access to the application manager's functionality through
the IAdphManager interface it passes to StartListenerChannel. In order to
determine the destination application for the activation request, the PPH calls
back to WAS and asks for the data blob that the listener provided when it
originally called WebhostOpenListenerChannelInstance. After doing some
twiddling on the returned byte array, the WCF PPH has access to the application
key that uniquely identifies the destination application (for example,
/w3svc/1/Foo) because, by convention, the WCF listener includes the application
key in the data blob it sends to the listener channel. WAS places no
restrictions on the data passed in the blob—from the WAS perspective, it's
merely an opaque byte array. It can then pass the application key, the protocol
key, and the listener callback interface to
IAdphManager.StartAppDomainProtocolListenerChannel to activate the
application's application domain.
In general, the
application manager uses the application key as an index into a table of all
application domains. When it receives a call to StartAppDomainProtocolListener,
it first consults this table to see if the application has already been
activated by a previous request that arrived over a different protocol. Finding
none, it uses the protocol key to look up the protocol-specific application
domain protocol handler type in much the same way as the process host used this
key to determine the process protocol handler type. Once the application domain
protocol handler type is resolved, the application manager creates a new
instance of it in the target application domain. Finally, the application
manager calls AppDomainProtocolHandler.StartListenerChannel, again passing the
WAS callback interface so the application domain can communicate with WAS if
necessary.
Once the
application domain protocol handler is instantiated, the internals of the
worker process look like Figure 6. At this point, WAS considers the target
application to be fully activated and has no further involvement in the process
until the application shuts down.
Figure 6 Application Domain Protocol Handler
Initialization
Getting Data from the Listener to the Worker
Activating worker
processes and application domains is the first step towards a larger goal:
actually delivering the message to the application. WAS, since it is an
activation service and not a message delivery service, does not participate in
this activity. Instead, each protocol implementation is free to implement
communication between the listener and the worker in whatever way it sees fit.
This architecture allows for various protocol-specific optimizations and for
each implementation to exploit the nature of the underlying protocol. For
example, the WCF MSMQ activation service simply passes the name of the MSMQ
queue in the data blob, allowing the activated application to read directly
from the data source and bypass the listener completely.
Pasted
from <http://msdn.microsoft.com/en-us/magazine/cc163357.aspx>
<<Prev
No comments:
Post a Comment