Link to home
Start Free TrialLog in
Avatar of mrichmon
mrichmon

asked on

Central on/off switch for entire websites

Using ASP.NET 2.0 and C#.  Windows 2003 servers.

I would like to have a central on/off switch for an entire website.

Ideally it would work as follows:

Administrator would log onto a webpage.  From that webpage there would be a list of sites.  Each site could be toggled on or off.  There would also be the ability to toggle all sites at once, say using a loop or something.

When a site was turned to "on" it would function normally.
When a site was turned to "off" any time it was accessed it would redirect to a "site down" page.

A few restrictions/requirements for a solution:
1) The switch cannot be stored in a database.  Most often the reason the site will be down/unavailable is if the database needs maintenance
2) There should be no code needed on a per page basis in the site.  It should be transparent from the individual page's point of view.

Feature Request: On/off master control page may be located on a different server.

You can assume that the web server is up and functioning - we are not worrying about that scenario.
You can assume that a person has to manually toggle the switch.  It does not need to detect resources being unavailable at this time.


For example, in Cold Fusion there is a file called Application.cfm that is called before/when any page loads.  This code could be put there.

Possible ideas I have had include somehow setting something in the web.config, but I have no idea how that would work.

Any ideas would be appreciated.
Avatar of AGBrown
AGBrown
Flag of United Kingdom of Great Britain and Northern Ireland image

If your site is offline then you should also assume that that means that the ASP.NET engine for the site is offline (because of possible site upgrade etc). This would mean that you couldn't rely on any of the ASP.NET mechanisms to do this. You may want to discard that as too restrictive, in which case you could use the Global.asax code file to check for a flag, possibly on an web service somewhere. This would let you store that flag in a seperate database to the application database, or by using another mechanism such as a file on the web-service.

You may also want to limit each application to checking only once every 60 seconds (or even every 5 minutes). For scheduled maintenance, this would be acceptable. You could extend the functionality of the web service to have scheduled offline times so that the admin can say "offline from 6pm tonight", come back at 6:01 and all the sites will be offline. You should also assume, if the web service is unavailable (who maintains the maintainer :-) ) that you are allowed to be online.

In terms of the redirect, this would easily allow you to redirect your responses to any page anywhere.

If you have to also assume that the ASP.NET site itself is down for long periods of time, then you need to look at some other mechanism. You could consider a custom ISAPI filter, or you could make use of WMI to actually set the IIS properties of the site to redirect to a different page instead of using the folder for the application.

Another alternative, given the work possibly involved in the ISAPI filter, and the security concerns that your admins may have with allowing WMI access from an (albeit internal admin) website, would be to use a third party product. I'm willing to bet there are plenty available, I just wouldn't know what they are.

Andy
Avatar of bullrout
bullrout

Hi There,

It's possible to start/stop/pause iis websites remotely
1) http://www.microsoft.com/technet/prodtechnol/WindowsServer2003/Library/IIS/694960cd-9d7f-4ffb-a6b9-2ea64255d463.mspx?mfr=true

2) in regards to the redirection of traffic when the website is no longer available, you could set a customer error page in iis to inform the user that the website is unavailable.

Hope this helps, Sean ;-)
Avatar of mrichmon

ASKER

AGBrown,

>>If your site is offline then you should also assume that that means that the ASP.NET engine for the site is offline
No.  Notice I said that "you can assume that the web server is up and functioning - we are not worrying about that scenario."

We are not looking for something to cover all situations of a site going down.  We just want a way to flip a "switch" that causes all pages on the website to redirect to a specified down page.  Therefore we are looking for a .NET solution.


bullrout,
You misunderstood the question.  I am not looking to stop the website in IIS or redirect using a custom error page.

Think of it this way.  We have Pages 1 - 10 of a web application.  Users could be at any point moving from 1-10.  We also have a page called "Down" which is where you go first before hitting page 1.  (Think of it as page 0).  We want to be able to force everyone to page 0 no matter where they were in the 1-10 process if the flag is turned on.


AGBrown,

Can you give me more information on webservices.  I haven't programmed them myself yet - just beginning to look into them, so don't really understand them yet.
ASKER CERTIFIED SOLUTION
Avatar of AGBrown
AGBrown
Flag of United Kingdom of Great Britain and Northern Ireland image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
I have been thinking a bit more.  One thing is that the flag cannot be in a database.  Most often the reason we will use this is the database servers going down.

But assuming I had the flag ans was able to call it - suppose even using a web service or something else - where in the global.asax file would it go that it would be run for every page?

Would it be better to go in the Application_BeginRequest or in the  Application_PreRequestHandlerExecute area?

I am not too clear on the difference between these other than the order they execute...
Okay you were right about web services being easy.

Also you mentioned "You should also assume, if the web service is unavailable that you are allowed to be online."

I like that idea.  Would I test that by a simple try/catch around the code?

try
{
   bool sitedown = mywebservice.testSite("thisSiteId");
   if(sitedown)
       Response.Redirect("\downpage.aspx");
}
catch{}

Or is there some other method to detect a failed web service.
To answer the first question first, i reckon you just dump it in the Application_BeginRequest, as it claims to:
"Occurs as the first event in the HTTP pipeline chain of execution when ASP.NET responds to a request."

So you could do something like "Response.Redirect("blah you can't get online");" and send them somewhere else.

W.r.t how to find out if it's unavailable, well, you should do the try/catch anyway (make sure you log the exceptions somewhere so you know if it is persistently failing), but I think it is the best way anyway. I think you can set the timeout on a web proxy, so i would make sure it is something short (but not too short). You might also consider creating a class that got stored in the application state on application startup (using global again) that had a server timer in it, and just pinged the web service regularly using the asynchronous methods in the proxy. That would expose a property for the BeginRequest method to check on each time, which would be updated when the web service returned from the asynchronous get. If that property was read-only then you wouldn't have to worry about locking the class at all when each request read the IsOnline property.

For storing your flag, if you can't use a database, you could use an Xml (or other format, it wouldn't really matter what it was) file store that just listed the id/name/guid of the application that is unavailable. Anything not in the xml file would be assumed to be available. The possibilities are endless, though I would be tempted to make the admin web app tell the web service when something is offline. The web service would update it's in-memory cache, and the file on disk, and then return succesfully to the admin web app. That way if the web service restarts, it can load the info from a file on startup.
I was thinking of storing the flag in each individual web config file.  Or I could do xml - that isn't really the hard part of this for me.  Many options, all I have under control. :o)

I have a logging mechanism set up so that isn't a problem - but a good idea.


>>I think you can set the timeout on a web proxy
That is a little over my head with how new I am to web services :o)  Can you explain what you mean by that.


>>You might consider creating a class stored in the application state on application startup that had a server timer in it
Yes that is what I was thinking


>>and just pinged the web service regularly using the asynchronous methods in the proxy
Hmm... I wasn't thinking that.  I was thinking that it would just check the timer to see if it should call the webrequest to refresh the bool or just use the existing value

How would you "ping regularly"?  Is there a place that can do this - I have not seen one - or would it be limited to running on requests of pages?  If limted to requests of pages, then what is the purpose of using an asynchronous method?

>>If that property was read-only then you wouldn't have to worry about locking the class at all when each request read the IsOnline property.
How would you do that?  It seems to me that it cannot be read only since you would be updating it.  Am I missing something?

Even if you put it in a getter type property you still have to write the value at some point correct?  ANd have to worry about concurrency there...  Unless of course I just save the web request to a local variable, then lock the applicaiton scope, then write, then unlock.  Does locking prevent reads as well?

But again then where does the readonly come into play?

Thanks
>> >> I think you can set the timeout on a web proxy
>> That is a little over my head with how new I am to web services :o)  Can you explain what you mean by that.
When you autogenerate the web reference (right click on the project that wants to call the web service, add web reference, follow the prompts - note that I usually give it a more meaningful name than just localhost or whatever it comes up with) then Visual Studio generates a web service "proxy". Its just a class that you can call as if it were a normal class and not a web reference. That proxy class has a Timeout property you can use.

Lets say you have a console application, and a web service application in your solution. The web service application has a service.asmx file. When you add a web reference to your console application, if you choose "web services in this solution" (assuming VS2005) or a web reference from your localhost (assuming 2k5 or 2k3) the name it will give the web reference is most likely localhost. Lets say you don't change that (though I would) then this code would set the timeout for the proxy, and call the hello world method:

    localhost.Service srv = new localhost.Service();
    srv.Timeout = 5000; // 5000 milliseconds = 5 seconds
    Console.WriteLine(srv.HelloWorld());

>> >> and just pinged the web service regularly using the asynchronous methods in the proxy
>> Hmm... I wasn't thinking that.  I was thinking that it would just check the timer to see if it should call the webrequest to refresh the bool or just use the existing value
Plenty of ways to make an omelette ;). When I say "ping" I don't mean ping as in the ping in the command prompt (though you could do that http://www.eggheadcafe.com/articles/20020209.asp). As we both say, you could store a class instance in your applicationstate that had a server timer in it. This would run regardless of requests coming in. I would just set the timer for, say, 5 minute intervals, and inside your stored class, write the event handler for the timer's Elapsed event to check your status service.

You could get even cleverer than that, such as, during the Elapsed event handler, check to see if you have had any site activity in the last twenty minutes. If not, then don't bother checking the web service (to reduce load on the web service). This would then need something in the property get for the IsOnline property to say "if I haven't checked in the last five minutes, check now", as it is possible that your first request after half an hour would have an out of date value for IsOnline. The idle timeout should probabaly be at least longer than the session timeout for your site.

When I say a read-only property I mean a property that is publicaly read-only, but privately writeable. Your web-service checking class that is stored in the applicationstate would have the following:
private bool _IsOnline = true; // can be updated by this class only
public bool IsOnline { get { return this._IsOnline; } } // can only be read by external classes

Threading concurrency is not important to you for this. If you consider the case of 1000 requests to your application per second (extreme, maybe!). Every five minutes you pick up a value from your web service. Does it matter if the 300,000th or the 300,001th request gets told "I am now offline". Also, that request to the web service could itself take 20 seconds if you are experiencing high bandwidth, which adds another 20,000 requests that slip through.

Finally, I mentioned asynchronous checking. This is again because of this, or similar scenarios:
1) Someone asks for a page
2) Application worker process had shut down due to inactivity, so starts up
3) Therefore Application_Start creates the IsOnlineCheckingClass, stores it in the application state and (synchronously) makes a request to your web service, which takes 20 seconds to respond
End result is the user has waited for some time for the application worker process to start, and another 20 seconds for the IsOnlineCheckingClass to return

or

1) Someone asks for the first request after 30 minutes
2) IsOnlineCheckingClass hasn't checked for 20 minutes, so decides it has to when the Aplication_BeginRequest method calls the IsOnline property. Again, this takes a while.
End result is a slow response to the user again, and they might give up and go away.

This is really for you to work out what is acceptable. If you do an asynchronous check (99.9 % of the time the answer will be "I am online") then you risk letting someone through when you are already offline.

Did I cover everything? I think I did.
I appreciate the long responses.  I'd give you more points if I could for this, but can't since it is already at 500.

However, I am still unclear on a few points.

>>As we both say, you could store a class instance in your applicationstate that had a server timer in it.  This would run regardless of requests coming in. I would just set the timer for, say, 5 minute intervals, and inside your stored class, write the event handler for the timer's Elapsed event to check your status service.

Well actually I was referring to a timestamp.  I had not heard of a "server timer".  I've looked it up now and your post makes more sense.  I would think that I could also do something where if the flag was online only check every 5 minutes, but if offline change the interval to a smaller number so it would check that it is back online faster.

>>When I say a read-only property I mean a property that is publicaly read-only, but privately writeable. Your web-service checking class that is stored in the applicationstate

Yes I understand that but it still seems like concurrecy is an issue.  Because this object (the instance of the IsOnlineCheckingClass) is stored in the application scope.  Therfore if someone were reading it as it was writing it's internal value (since that happens on its own via the timer I think), then wouldn't ther ebe the possibility of corrupt value being read mid-write?  I am not as concerned about reading the wrong value as I am about reading at the exact wrong moment and getting a corrupt value.

>>Application worker process had shut down due to inactivity, so starts up
Can that actually happen?  And cause Application_Start to re-run?  I was under the impression that the application_start only ran when the application started up and otherwise the application state would only restart as a result of crashes, code updates, scheduled process restarts.  Are you saying it can also restart due to inactivity?

But overall I think asynchronous will work since if a few get the wrong info, they should get the correct info by the next request.

Thanks for the comments - I am working on setting up a test environment.
Also see here http:Q_21840008.html for more detailed question about the timer aspect
I see what you mean about the timestamp. Actually, if you were doing a synchronous approach, then a timestamp would be just as effective, and a lot simpler.

You won't get a corrupt read on that variable if you don't lock the class, I'm pretty sure of that. Behind the scenes, the checking code first copies from _IsOnline to the register. Then it changes it, and then it writes it back. Even if another class reads that value between the copy and the write-back, it's only like if read it just before the copy, and for this scenario, that isn't important. Locking takes up resources, and you should only use it where necessary.

Your worker process can be shut down for any number of reasons, depending on the IIS settings. The worker processes can be recycled on:
-regular interval
-number of requests
-set times
-memory level triggers
And can be shut down on inactivity for a certain amount of time.

You should code your application to take account of all of these, as it might not be you who administers the application pools on the web server.

Having slept on it, the timer idea has legs as you don't have to check the timestamp in each IsOffline get statement, but there are lots of other potential problems. Imagine that your application was "taken offline", and while that is happening the worker process shut down due to inactivity, or server reboot. Then someone requests a page. The application starts up, and the checking class asyncronously checks for online/offline. While doing so it lets the user onto the app.

You could solve that by persisting a local copy of the flag to be used as the default when the application next starts. But, you can see that if the application worker process went idle _before_ that flag was set to offline, then it's stored value would still be Online, and the user would still get through.

If your web service is on the same local network as the web servers, then I might be tempted to go for a synchronous approach. That doesn't preclude the use of the timer-based approach though, you could use timer/timestamp with either synchronous/asynchronous.

I'll take a look a the timer question, but probably won't get a chance until monday now - sorry.

Andy
Not a problem.  I realize this is a volunteer thing and you do have other things to be doing :o)
Okay well I set up a test on my site.

The servers will all be on the same local network.  I wasn't able to get the Timer working so for now I did a timestamp and locked around the update.  Maybe we can talk about that in the other question.

Also, my detection class is in a central class library - at least the definition.  Therefore I couldn't get access to the BeginXXX and EndXXX methods to do it asynchronously.  If I do i from a web page not a class library I do get those options so I know the web service is written properly.

So for now it is synchronous, but very fast as far as I can tell.

Thanks for the help.