In my last post, I gave a bit of background on the Application Warm-Up module, now called Application Initialization. This week, I would like to go into more detail as to what the Application Initialization module does, and how you should think about using it.
As I mentioned earlier, the idea behind Application Initialization is that we want to provide a way that IIS can prepare an application to serve requests without having to wait for an actual client to make a request. With Application Initialization, we break the problem down into 3 parts:
- How can I start the worker process that hosts my application without waiting for a request?
- How can I get the worker process to load my application without waiting for a request?
- How can my application send some kind of response so that clients don't see the browser hang until the application is ready?
I would like to address the first two questions here. The third question is a bit more complex and I will save it for my next post.
Starting a worker process without waiting for a request
This is something that's not strictly speaking a part of Application Initialization in that we added this capability as a built-in feature of IIS, starting with IIS 7.5. I will go over it here because it works hand in hand with Application Initialization to make the application available as soon as possible after starting IIS.
This feature is controlled by a the startMode property for the application pool, described (along with other application pool properties) here. The default value for startMode is OnDemand, which means that IIS will not spin up any worker processes until needed to satisfy a client request. If you set it to alwaysRunning, IIS will ensure that a worker process is always running for the application pool. This means that IIS will spin up a worker process when the World Wide Web Service is started, and it will start a new worker process if the existing one is terminated.
Note that this property should not be confused with the autoStart property. Understanding autoStart requires a bit of background knowledge. Both application pools and worker processes can be started and stopped. If an application pool is started, it means that IIS will accept requests for URLs within the pool, but it does not necessarily mean that there are any worker processes started. If an application pool is stopped, IIS will return a "503 Service Unavailable" for any requests to the application pool and it will not start any worker processes. The autoStart property is essentially a flag that IIS uses to know which application pools should be started when the World Wide Web Service is started. When you stop an application pool in IIS Manager, autoStart is set to false. When you start an application pool, autoStart is set to true. In this way, IIS ensures that the same set of application pools are running after the World Wide Web Service is started and stopped (or through a machine reboot.)
Now let's take a quick look at the configuration for an application pool that is set to be always available. This application pool will start when the World Wide Web Service starts and it will immediately spin up a worker process.
<add name="DefaultAppPool" autoStart="true" startMode="alwaysRunning" />
With this configuration, the Default Application Pool will immediately spin up a worker process when IIS is started, and it will spin up a new worker process when the existing one exits.
With IIS 7.5, this property was not exposed in IIS Manager. It can be set by editing the applicationhost.config file directly or by one of IIS's scripting or programming APIs, or by the Configuration Editor UI tool. In IIS 8, we have added the startMode property to the advanced properties page for the application pools UI.
How can I get the worker process to load my application without waiting for a request?
Now that you can see how to get IIS to spin up a worker process without waiting for a request, the next thing to address is how to get an application loaded within that worker process without waiting for a request. The Application Initialization module provides a solution here, and as above, it is controlled by a single configuration property.
The Application Initialization module extends the IIS configuration by adding a new property to the application settings called preloadEnabled (in IIS 8, this property is built-in.) Let's take a look at what this looks like in the configuration where I've added a new application to the default web site and enabled it for preload:
<site name="Default Web Site" id="1">
<virtualDirectory path="/" physicalPath="%SystemDrive%\inetpub\wwwroot" />
<application name="AppInit" applicationPool="DefaultAppPool" preloadEnabled="true">
<virtualDirectory path="/AppInit" physicalPath="c:\inetpub\wwwroot\appinit" />
Here's how Application Initialization uses this property. When a new worker process spins up, Application Initialization will enumerate all of the applications that it will host and checks for this property. For any application where preloadEnabled="true", it will build a URL corresponding to the default page for the application and run it through the pipeline. This request does not go through the network, and there is no client listening for a response (IIS discards any data that would have gone to the client.)
This "fake" request accomplishes a few key things. First, it goes through the IIS pipeline and kicks off an application start event. This initializes a number of parts inside of IIS, and if the request is for ASP.NET, it will cause global.asax to run. It also reaches the application, which will see it is the first request after starting. Typically, I expect that applications will just handle this request just like any other request from a real client, but we do set some server variables into our "fake" request, so an application with awareness of this feature could implement special processing if it chose to do so.
There is another important aspect to this process. When IIS spins up a new worker process, there is two way communication between WAS and the new process. This allows WAS to know precisely when the worker process is ready to accept new requests. It also allows the worker process to get information from WAS as to whether it is going to be a new process to start taking requests, or whether it's a replacement process to take over for an older process that's being recycled.
This is an important distinction. In the case of a new worker process, we want to start taking client requests as soon as possible, which is the way that things work outside of Application Initialization. In the case of a replacement process, though, Application Initialization will prevent the new process from reporting itself ready for new requests, until all of the preload requests (and any warumup requests, which I will discuss later) have completed. This means that no client will ever have to wait for a process recycle to complete - because the old process will continue to take requests until the new one has completed all application initialization.
In my experience, many applications with a slow startup will do their work even for a simple request to the default page. For such applications, you can take advantage of improved application recycling simply by setting preloadEnabled="true" for that application. Similar to the startMode property above, IIS 7.5 requires you to make this setting via direct edits or applicationhost.config, or via scripting or one of our config APIs, or via the Configuration Editor UI tool. In IIS 8, we have added "Enable Preload" as a checkbox in the UI for application settings.
The two topics that I've covered here should get you started with Application Initialization. The ability to handle worker process recycles has been a highly requested feature.
In my next post, I'll tackle the topic of what it means to initialize an application and what things an application developer can do to make things responsive during the time everything is warming up. This is where we've made major changes and added a lot of stuff since the original beta release.