Archive
Support Architecture for your Web Application
Whenever I start a new project, in particular a web project of some kind, there are several steps that I take in preparation for scaling the project or supporting different business functions. Let’s face it, not everything that the website might expect to do should be done by the website; a classic example of this might be notification that a subscription is about to expire. That sort of occasionally scheduled business process is certainly doable in the context of web development, but in general it’s not recommended.
One that might be less apparent might be tasks such as general email delivery, say a forgot password email, where under high traffic one might want to off-load the email delivery to a secondary process rather than hold up rendering of a web page to the browser while the email is built, formatted, and ultimately delivered.
To support a wide variety of potential offline processes, I generally will setup what I might refer to as “harnesses” for three different types of processing. These harnesses are fairly generic, interacting with an interface implementation and typically based of the standard .NET configuration model for instantiation of the class implementing the interface. In many cases, the interface implementation is common to all three harnesses, such that the processes are interchangeable.
There are three basic timing elements for things you might want to accomplish offline from your website. The first timing element I would refer to as “do something repeatedly every so often”. The second timing element I would refer to as “do something on a scheduled basis”, whether that schedule is every few hours or once a day or even once a month. The last timing element I generally prepare for is a “one off” or “one time” execution.
It should be noted that this sort of architecture presupposes that you have full control of your computing environment either through ownership of the servers or access via some sort of cloud computing or virtual hosting service, such that you can install and run items from the console or command line. Obviously this would not be possible if all you had was a web host for your website.
Under those assumptions, I will create three things. First, I will create a Windows Service. This service’s sole purpose would be to take a configured set of objects that implement my interface and run them repeatedly at a specified interval, say once every three minutes. A good example of this might be a process that monitors an email inbox for new messages and processes them in some fashion. Because this is a Windows Service, it might be wise to give each object its own thread, or if you are the latest .NET platform, its own Task.
Second, I will create a standalone console application that I intend to schedule to run regularly. This console application will also load up a configured set of interfaced objects and run them a single time when the scheduled task executes. A good example of this might be some sort of nightly statistical analysis that needs to be done for reporting. In the same sense as the Windows Service, if you have a lot of objects, it might be wise to allow for sequencing some of them in order while noting which ones are truly independent, and then multi-threading them or assigning them Tasks in the proper order.
Last, I will create a near-replica of the standalone console application above, but likely without the multi-threading in place as this is considered to be a one time execution. The application above and this one might even go so far as to be exact copies deployed separately, with one scheduled and one not, if the different requirements for each don’t stray. Common uses of this would be for one-time data conversions, say for example you had inherited a poor database structure that had a person’s name all in one field and you wanted to split into first and last name.
Once I have these three harnesses built, it becomes very easy to generate a plug-and-play approach to any tasks that would need to be scheduled or executed in a predictable fashion without constantly creating new services or new executables to handle the work. This would then allow me to very easily move tasks that my web project might eventually find overwhelming or detrimental to performance off into background tasks without constantly reinventing the wheel. It also allows me, if I so choose, to install or write some code to monitor the execution of these harnesses without having to rewrite the monitoring code every time as well.
This approach has been helpful on several projects and saved me a lot of work down the line.
Dynamically Controlling Windows Services Using SC.exe
There are many instances where you would like to have remote control over Windows Services. In fact, during deployments, you might want to even have the ability to remove and reinstall Windows Services on the fly, or even go so far as to install multiple copies of the same service under different names and/or configurations.
These requirements go beyond the ability of the native installation procedures that are included with Visual Studio or the framework (such as InstallUtil.exe). However, with SC.exe, you can have complete control over Windows Services, and even install them with different names and so forth.
SC.exe comes with the Resource Kit and runs from the command line. It takes a variety of arguments, including a command, the service name, the path to the file, the display name, and the machine to execute against.
sc [Servername] Command Servicename [Optionname= Optionvalue…]
It’s important to note that the space after the “=” in the Optionname key value pair DOES exist and is not a typo! You’ll see that in the examples below. Most of us have a tendency (myself included) to look at that and assume there’s no way that the space should be there, but it is.
The servername is optional; if not provided this will run against the local machine.
There are many commands you can give, but the important ones are:
Start
Starts a service
Stop
Sends a Stop request to a service.
Create
Creates a service (adds it to the registry).
Delete
Deletes a service (from the registry).
It’s important to note that the ServiceName here is the actual ServiceName, NOT the Display Name, which is generally what you use when issuing Net Start commands.
So, if we wanted to stop the BlogReaderService on BlogBox1 from BlogBox2, at the command line on BlogBox2 we would enter:
sc BlogBox1 Stop BlogReaderService
There are several options available as well for the executable. Here are a few of the important ones.
start=boot, system, auto, demand, disabled
Start type for the service. Option values include types used by drivers.
(default = demand)
binPath=(string)
Path name to the service binary file. There is no default for this parameter. This string must be supplied.
DisplayName=(string)
A string that can be used by user-interface programs to identify the service.
So let’s assume that during deployment I want to stop, delete, create, and start my BlogReaderService. My BlogReaderService.exe sits in my c:\temp folder. I want the BlogReaderService to start automatically, and want its DisplayName to be MyBlogReaderService. Assuming I am on BlogBox2 and I want this to occur on BlogBox1, the four commands would look like this:
sc BlogBox1 Stop BlogReaderService
sc BlogBox1 Delete BlogReaderService
sc BlogBox1 Create BlogReaderService start= auto binPath= c:\temp\blogreaderservice.exe DisplayName= MyBlogReaderSerivce
sc BlogBox1 Start BlogReaderService
Note the spaces after the “=” in the command line…
Now that I have that, let’s assume I have a second copy of blogreaderservice.exe sitting in c:\temp2 with it’s own blogreaderservice.exe.config pointed a different datasource. Now I want to install it, but I need a different service name and display name and start it because the service is already there from the exe in c:\temp. That command line would look like this.
sc BlogBox1 Create SecondBlogReaderService start= auto binPath= c:\temp2\blogreaderservice.exe DisplayName= MySecondBlogReaderSerivce
sc BlogBox1 Start SecondBlogReaderService
And there you are…two copies of the service installed under different names.
Have fun!
Rob