Web Garden and Static Objects difficult to understand - iis-7

I am in the process of investigating to convert our web application to the web farm. So I started with web garden, by converting the 'Maximum Worker Process = 3'. Following is the simplified version of my problem.
Following is my Static Object class.
public static class MyStaticObject
{
public static string MyProperty {get;set;}
}
Then on Page Load I initialized the Static Objects as follows -
MyStaticObject.MyProperty = "My Static Property";
Then using asp.net ajax [WebMethod] create the ajax method on my web page
[WebMethod()]
public static string getStaticProperty()
{
return MyStaticObject.MyProperty;
}
// Then I call this Ajax method using Javascript and set the return to the text box.
This test is not working as expected. Following are my assumptions and wrong outcome from the test.
I thought when we set virtual directory to be web garden, then each request to the virtual directory is handled by different process in web garden, so my next few request to the server, should return null as I have initialized the static objects for one working process. But even if I click the ajax button for 20 times in row (means 20 requests) even then the static objects return me value.
Am i right in assuming on restarting the IIS should kill all the static objects.
Static objects are not shared in web gardens/web farms.
I am surprised by the behaviour of IIS, static objects and web garden.
Is I am assuming wrong or my way of testing is wrong.
Thanks.

Your assumptions about the way static objects are managed in AppPools / web gardens is correct.
However, your assumption about the way that web requests are distributed is not. HTTP requests are round-robined by the http.sys driver to IIS worker processes only when a new TCP connection is established, not when a new request arrives. Since keepalives are enabled by default, even though you made 20 requests in a row, they probably were all served by the same IIS worker process.
You can have IIS disable keepalives for testing purposes from the HTTP Response Headers section in IIS Manager, under Set Common Headers. That should cause your browser to open a new connection for each request.
To test with keepalives enabled, you can use the Web Capacity Analysis Tool (WCAT), available with the IIS 6 Resource Kit, to generate a multi-threaded load that accesses both IIS processes in your web garden.

Related

Pre-Load Web Application Pages After Deployment to Prevent Slow Loading

We build and deploy our web application to our dev environment automatically every night (using VSTS). When we come into the office in the morning, the first person to access the application has to wait an extended period for each page to load the first time. Subsequent loads are very fast.
The problem has a greater impact in our live environment where, after a deployment, it is potentially an end-user who is the first person to access the application and complain of slowness. To mitigate for this, a member of the team is currently accessing every page of the application manually after deployment to the live environment so that they 'pre-load' every page, which works, but is obviously time-consuming!
I've done a fair bit of searching on the subject, and have configured the appropriate Application Pool in our IIS server (IIS 8.5) so that its Start Mode is set to "AlwaysRunning". I've also edited our applicationHost file and set the appropriate Sites with the preloadEnabled="true" attribute. I did this after reading the instructions in this very helpful Microsoft documentation.
However, if I'm reading that documentation correctly, any pre-loading of the website which might alleviate the issue we're having (and I'm not even certain that this is the kind of pre-loading that I'm thinking of) only takes place when the server, the IIS service of the Application Pool are restarted. This isn't happening in our case. We need the pre-loading to take place following a deployment of the application to the IIS server.
Is there a way to automate this pre-loading?
One way of doing this would be to perform a HTTP request automatically:
As soon as the app was deployed (by running a task from the deploying machine)
Before the application pool has the chance to shut itself down (using Task Scheduler for instance)
Personally, I use a tool that is run in both cases to keep the site warmed up.
Advantages
Robust control over how and when this warm-up is executed.
It's completely independent from any IIS or web.config setup.
Disadvantages
Generates "bogus" log information.
Keeps the app permanently in memory (the Pool would never time-out, essentially wasting server resources for sites with a low # of visitors).
Sample
Such a tool could be a simple console app written as follows:
var taskInfo = new {
Url = "http://www.a-website-to-keep-warm.url",
UseHostHeader = true,
HostHeader = "www.a-website-to-keep-warm.url",
HttpMethod = "head"
};
HttpStatusCode statusCode = HttpStatusCode.Unused;
long contentLength = 0;
try
{
Dictionary<string, string> headers = new Dictionary<string, string>();
HttpWebRequest webRequest = (HttpWebRequest)WebRequest.Create(taskInfo.Url);
webRequest.Method = taskInfo.HttpMethod.ToUpper();
if(taskInfo.UseHostHeader)
webRequest.Host = taskInfo.HostHeader;
using (HttpWebResponse webResponse = (HttpWebResponse)webRequest.GetResponse())
{
//did we warm-up the site successfully?
statusCode = webResponse.StatusCode;
contentLength = webResponse.ContentLength;
//optionally read response headers
foreach (string header in webResponse.Headers)
{
headers.Add(header, webResponse.Headers[header]);
}
}
decimal kilobytes = Math.Round(contentLength / 1024M, 1);
Debug.WriteLine($"Got {kilobytes:F1} kB with statuscode: \"{statusCode} \" ...");
}
catch (Exception ex)
{
Debug.WriteLine($"taskInfo failed with exception: {ex.Message}");
}
In my case, I read a bunch of taskInfo objects from a json file and execute them asynchronously every X minutes, making sure X is lower than the Pool-timeout value. It is also run immediately after every deploy.
Because we're not interested in getting the entire content, it uses a HTTP HEAD request instead of GET. Lastly, it supports multiple sites on the same host by adding a Host header to the request.

Get URL information in Startup

In an asp.net MVC 5 project I'm using a katana owin based middlewere to handle the authentication. Inside the Start.cs file I have the Startup class with the Configuration method.
Is there a way to get the full URL of the request inside the Configuration method? I need to get the last part of it to be stored in a cookie
public void Configuration(IAppBuilder app) {
app.UseCookieAuthentication(new CookieAuthenticationOptions { ... }
// something here to get the full URL
// other authentication code here
}
Startup runs outside of the request-cycle. In fact, it only runs once, and then multiple successive URLs can be serviced before it ever runs again (when AppPool recycles, server restarts, etc.)
Long and short, even if you could access the URL, it wouldn't do you any good because it would simply be the first random URL that was accessed, which may or may not be applicable to whatever you're trying to do here.

Web service (asmx) POST test fails on specific load balanced servers

I have a ASP.NET WebApp that contains some ASMX webservices. We recently migrated to load balanced Windows 2008 servers from a Windows 2003 server. The new servers sit behind some F5 appliance for the load balancing (that's all I know about it!).
We can reach the built in ASP.NET POST test (example: http://webapp.company.com/webservices/Person.asmx?op=GetPerson) but invoking it from that test page fails (page not available). A new tab in the browser opens with the same url, but with a port number appended on the end: http://webapp.company.com:50831/webservices/Person.asmx?op=GetPerson. When I put that URL(with port) into my browser, it fails as well. Heck, http://webapp.company.com:50831 isn't reachable at all.
We didn't get that on our previous server. I setup my own personal webserver on a personal Windows 2012 server and tested the same code on there and it worked. So I'm thinking it has to do with the load balancing.
Unfortunately, I have no control over the web servers our company offers for hosting internal applications. I don't get to touch the IIS either. All I get is a file path to publish my files to. The hosting organization is telling me my ASP.NET WebApp code is appending the port number, but I don't think that's right. It only occurs on those load balanced servers!
Has anyone else ran into this before when invoking ASMX or WCF that's hosted behind a F5 appliance?
The IT group that maintains those servers didn't have any answers for us, but we were able to verify those odd ports were in fact being assigned by the F5 appliances. We ended up using a port rewriter as a workaround until the group that maintains the web servers can alter the configuration. The code we used is below which is taken straight from the URL below where they were discussing the same problem behind the same F5 appliance (BigIP).
http://www.justskins.com/forums/port-mapping-a-web-51075.html
public class WSDLAddress : SoapExtensionReflector
{
bool bFirstTime = true;
public override void ReflectMethod()
{
if (!bFirstTime) return;
bFirstTime = false;
SoapAddressBinding sabAddress = ReflectionContext.Port.Extensions.Find(typeof(SoapAddressBinding)) as SoapAddressBinding;
string sPort = ConfigurationSettings.AppSettings["myPort"];
UriBuilder uriLocation = new UriBuilder(sabAddress.Location);
uriLocation.Port = Int32.Parse(sPort);
sabAddress.Location = uriLocation.Uri.AbsoluteUri;
}
}

Can I get information about the IIS7 virtual directory from Application_Start?

I have 3 IIS7 virtual directories which point to the same physical directory. Each one has a unique host headers bound to it and each one runs in its own app pool. Ultimately, 3 instances of the same ASP.NET application.
In the Application_Start event handler of global.asax I would like to identify which instance of the application is running (to conditionally execute some code). Since the Request object is not available, I cannot interrogate the current URL so I would like to interrogate the binding information of the current virtual directory?
Since the host header binding is unique for each site, it would allow me to identify which application instance is starting up. Does anyone know how to do this or have a better suggestion?
When a request is made, and just prior to the HttpApplication instance being created, ASP.NET initializes core objects such as HttpContext, HttpRequest and HttpResponse which means they will exist when you get to the Application_Start event in Global.asax. Thus, in Application_Start, you can get the requesting url like so:
var url = this.Context.Request.Url;

How do I get the host domain name in ASP .NET without using HttpContext.Current.Request?

I've got an ASP .Net application running on IIS7. I'm using the current url that the site is running under to set some static properties on a class in my application. To do this, I'm getting the domain name using this (insde the class's static constructor):
var host = HttpContext.Current.Request.Url.Host;
And it works fine on my dev machine (windows XP / Cassini). However, when I deploy to IIS7, I get an exception: "Request is not available in this context".
I'm guessing this is because I'm using this code in the static constructor of an object, which is getting executed in IIS before any requests come in; and Cassini doesn't trigger the static constructor until a request happens. Now, I didn't originally like the idea of pulling the domain name from the Request for this very reason, but it was the only place I found it =)
So, does anyone know of another place that I can get the host domain name? I'm assuming that ASP .Net has got to be aware of it at some level independent of HttpRequests, I just don't know how to access it.
The reason that the domain is in the request is...that's what's being asked for. For example these are a few stackexchange sites from http://www.stackexchangesites.com/:
http://community.ecoanswers.com
http://www.appqanda.com
http://www.irosetta.com/
If you ping them, you'll see they all point to the same IP/Web Server and be served by the same app (or multiple apps in this case, but the example holds if it was one big one)...but the application doesn't know which one until a host header comes in with the request asking the server for that site. Each request may be to a different domain...so the application doesn't know it.
If however it doesn't change, you could store it as an appSetting in the web.config.
Use global.asax or write a HttpModule and subscribe to start request events. You will have the request passed into your event handler.
Use this instead:
HttpRuntime.AppDomainAppVirtualPath
Or if you want the physical path:
HttpRuntime.AppDomainAppPath
For further reading:
http://weblogs.asp.net/reganschroder/archive/2008/07/25/iis7-integrated-mode-request-is-not-available-in-this-context-exception-in-application-start.aspx

Resources