Saturday 10 December 2016

Azure Web App Application Initialization

If you have an Azure Web App that needs initializing before it starts receiving traffic and just calling the web server root "/" is not good enough (default behaviour) then you should be looking into custom "applicationInitialization". Before you read this article please take a look at the IIS Application Initialization documentation.

This article will cover 9 things that are not mentioned in the documentation.



This is how the web.config applicationInitialization XML config can look like:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <applicationInitialization doAppInitAfterRestart="true" skipManagedModules="true">
      <add initializationPage="/init1.aspx" hostName="somewebapp.azurewebsites.net"/>
      <add initializationPage="/init2.aspx" hostName="somewebapp.azurewebsites.net"/>
    </applicationInitialization>
  </system.webServer>
</configuration>
This config should be placed in the wwwroot.

What they don't mention in the documentation


1. You can have many initialization pages

As you can see in the example above you can actually initialize many pages e.g. init1.aspx, init2.aspx.

2. Initializations are done in order

As per above example, init1.aspx will be called first, then as soon as init1.aspx responds or times out it will move on to init2.aspx

3. Initializations are response ignorant

ApplicationInitilization doesn't care if init1.aspx returns 200, 404 or 503. As long as it returns something it will consider it's job done.
This means if your init1.aspx page times out, IIS will still think that you are good to go.

4. Can't have duplicate initialization page

You can't have duplicate keys, such as:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <applicationInitialization doAppInitAfterRestart="true" skipManagedModules="true">
      <add initializationPage="/init1.aspx" hostName="somewebapp.azurewebsites.net"/>
      <add initializationPage="/init1.aspx" hostName="somewebapp.azurewebsites.net"/>
    </applicationInitialization>
  </system.webServer>
</configuration>
This will make IIS error. You might think, why on earth would you want to have duplicate keys? This could be considered a workaround for the timeouts, if init1.aspx takes too long to respond, call it again, next time hopefully it will respond immediately. To workaround this you could just add query string:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <applicationInitialization doAppInitAfterRestart="true" skipManagedModules="true">
      <add initializationPage="/init1.aspx?call=1" hostName="somewebapp.azurewebsites.net"/>
      <add initializationPage="/init1.aspx?call=2" hostName="somewebapp.azurewebsites.net"/>
    </applicationInitialization>
  </system.webServer>
</configuration>

5. Initializations are done internally

IIS applicationInitialization requests are done internally so you will not see them in your W3C logs. However if your init1.aspx page looks at the request and takes a look at the User Agent it will see the following: "IIS Application Initialization Preload"

6. No client state is shared between calls

When IIS calls init1.aspx and then calls init2.aspx there is no common client state kept i.e. cookies, etc. This means you can't tag the IIS applicationInitialization client.

7. Initialization can warm up virtual applications


8. hostName is the same for all slots

This has really surprised me, one Web App can have many slots, each slot has it's own name, however you when you use initializationPage you can just specify the main slots hostName and this will work for all slots. So for example, if you have production slot and you have staging slot you might consider doing something like this:
Production Web.Confg
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <applicationInitialization doAppInitAfterRestart="true" skipManagedModules="true">
      <add initializationPage="/init1.aspx?call=1" hostName="somewebapp.azurewebsites.net"/>
      <add initializationPage="/init1.aspx?call=2" hostName="somewebapp.azurewebsites.net"/>
    </applicationInitialization>
  </system.webServer>
</configuration>
Staging Web.Confg
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <applicationInitialization doAppInitAfterRestart="true" skipManagedModules="true">
      <add initializationPage="/init1.aspx?call=1" hostName="somewebapp-staging.azurewebsites.net"/>
      <add initializationPage="/init1.aspx?call=2" hostName="somewebapp-staging.azurewebsites.net"/>
    </applicationInitialization>
  </system.webServer>
</configuration>
No need, this will work for both:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.webServer>
    <applicationInitialization doAppInitAfterRestart="true" skipManagedModules="true">
      <add initializationPage="/init1.aspx?call=1" hostName="somewebapp.azurewebsites.net"/>
      <add initializationPage="/init1.aspx?call=2" hostName="somewebapp.azurewebsites.net"/>
    </applicationInitialization>
  </system.webServer>
</configuration>
This is good news, as you will need to perform less transformations.

9. Load Balancer

This might be one of the most important points. IIS applicationInitialization and Azure Load Balancers work together. As soon as IIS initializes all of the pages, internal Azure Web Apps load balancer is notified and it starts sending traffic to the fully initialized instance. So you can use applicationInitialization to warm up your application completely before you allow traffic to come in.

Conclusion

If you have a large app and you need to invoke many different areas to warm up caches, compilations, etc before your instance starts receiving traffic then IIS applicationInitialization is a good way to go!

As I don't work for Microsoft I can't guarantee that my description of Azure Web App inner workings is 100% accurate.

5 comments:

  1. Hi, first of all thanks for the post its clear and useful. Btw i want to know if point 9 its confirmed. I have an azure webrole application with a load balancer probe configured and im experiencing some problems when the service scales. It seems current running instances restart (1 by 1 -> ok) but they become on ready state before the loadbalncer probes returns 200. So incoming requests fail because cache is not ready and requests produces timeouts.
    Thanks in advance

    ReplyDelete
    Replies
    1. Thank you for the comment! Point 9 is true for Azure Web Apps. I am not sure if it's true for other Azure services such as Azure Cloud Services.

      Delete
  2. Thank you very much! This was really helpful. I could not find anywhere what to set as 'hostName' when working with Azure slots.

    ReplyDelete
  3. What does skipManagedModules="true" really do?

    ReplyDelete
  4. Point 8 is not true. I confirmed by starting weblogs and checking which paths were hit. When I add the production host name, the deployment slot name is never hit.

    In addition, if your code redirects from http to https (or with a rewrite rule in the web.config), the initialization will occur using http, which will return a 307. Since the initialization routine doesn't respond to http return codes, the page is never redirected to https. The solution is that you have to make sure your slot is set up to automatically redirect to https rather than using rewrite rules.

    ReplyDelete