Setup the software
There is more to constructing a cluster than just the hardware. There is plenty of work to be done in the software as well. Depending on how your software is written you could cause the cluster to not function correctly or you can help your cluster scale to any size neccessary. As with the hardware and operating system setup there are many ways you can write your software in a clustered environment. You will always want to make sure the model you are using makes sense for the requirements you are dealing with in your own system. Here I will be providing a model that is very fast, secure, and flexible that works well in environments that have many clients calling methods on the cluster that interact with many databases distributed across a data tier. First I will handle the software neccessary to connect clients to the NLB cluster servers and then after that I will show how to build the software to connect the NLB cluster servers to the databases in such a way to make the databases "clustered" as well.
Service Architecture
Under most circumstances the best option for hosting your software on a NLB cluster server is to use Microsoft Internet Information Server (IIS). This method gives you alot of functionality and features that are already cooked into the operating system itself. There is also a big bonus on the coding side of things because writing DLLs that are hosted in IIS is much easier than writing your own Windows Service along with all the TCPIP handling. You even can get the benefit of Visual Studio being able to "publish" your web services directly to the server instead of a more complex distribution scenario. The web services in this model will be using IIS.
As discussed earlier when going over the affinity setting in NLB cluster setup a major consideration when building cluster capable software is "state management". In fact it can probably be accurately called the number one consideration. Load balancing is all about the ability to easily move users back and forth between servers in the cluster to keep the load evenly balanced. This means that a single user in a single session can make calls to the cluster and end up on a different server each time a call is made. What you dont want in a scenario like this is to have users dependant on or tied to any one server. This will either straight up break the application or at least slow the cluster down as the load can not be balanced well because of the need to route users to the servers they are tied to instead of directing calls to the server that is best for load balancing. The approach to this problem I take is to design an architecture that uses stateless web methods. Obviously state or session data must be maintained some where. It is just that in a stateless web service system that data is not stored on the NLB cluster servers. Instead the state is stored in databases in the data tier. So as the code is being written we will make sure that each call to a web method builds up all its variables, connections to the database(s), etc. from scratch.
Over the years web browsers have become increasingly powerful client side application hosts. Web developers have long taken advantage of Dynamic HTML and Javascript to make reasonably advanced client UIs. Now the new term for this technology is called AJAX and is pushing the browser even further. However desktop applications are still far more sophisticated and powerful than browser based front ends. Because of this my example here is going to present a Windows Smart Client application as the front end. With enough effort a Javascript equivilant could be built to interface with the web services in your cluster that also will work with Smart Clients. You could also create seperate web services for the web browser clients or even host a Smart Client in the web browser if you absolutely need to use a web browser. Later in the article I will show how to create clients in web browsers that use Javascript and the XMLHttpRequest object to communicate with XML web services hosted in the same cluster as the Binary HTTP web services.
Also because of the inheriant ineffiency of XML web services compared to binary only solutions I will be using Remoting to communicate between the client and the cluster servers. Due to using fully functional C# Smart Clients and Remoting the communication library will also incorporate data encryption and compression to both protect data traveling between the client and servers as well as compress the data for a further performance increase. Whenever possible more techniques will be employed to further increase performance. The more performance that can be squeezed out of the code the more load the cluster can handle before needing more servers to be added. Much of the performance advantage of Smart Clients over Browser clients comes from the fact that the client UI already exists on the client PC as the application is running instead of being downloaded on the fly (many times over and over) at runtime from the servers like a web page normally is. This leaves just the data itself to travel back and forth between the client and servers and then this data gets compressed to make the process even more efficient. And finally Smart Clients can communicate via http port 80 using Remoting just like a Web Browser can. This is called Binary HTTP communication. Easily passing through firewalls just like any other web page.
Adding web services to the cluster
So what are the parts of a web service hosted in IIS?
-
A virtual directory in IIS
-
a web.config file in the virtual directory
-
a folder called bin in the virtual directory
-
one or more dll files in the bin directory
That is pretty much it. Of coarse there are details all over inside those items, but that is the basic layout. So what are some of these details for each of the items above?
Virtual directory:
-
The virtual directory can have IIS settings made that determines the authentication type used to access the service.
-
An IP access list can be set that prevents clients from calling the service if their IP address is not in the allowed list.
-
HTTP Keep alives can be turned on or off
Web.config:
-
Settings in the config file tells IIS how to create and maintain the web service.
-
Database connection strings can be maintained here.
-
Various other settings that govern other functions such as logging for the web service are set here.
Bin folder and its DLLs:
-
The dll files in the bin folder include, but are not limited to, the dll that contains the funtionality for the web service.
-
A dll file that exists on the client and the server in this bin folder that contains the "component interfaces" that connect the web service to the client side code.
-
And any referenced third party library dll files used by the web service dll to add more functionality.
The basic class model for communicating over the Internet using Binary HTTP Remoting is quite simple. Below is an illustration of this model. Lets go over the three main components of this model. From the client perspective we have an object that "stands in for" or "represents" the Remoting Web Service on the cluster servers. This object is called a proxy. Whenever the client code wants to call the remoting web service it instead calls the proxy and the proxy then makes the actual call over the Internet to the web service. A proxy itself is like a faceless formless being. In order to make it useful you connect it to an interface and then call the methods on the interface. These calls do not actually get made at this point. Instead the calls are "captured" and then packaged up with all the parameters passed into the method and this package is sent across the Internet to the web service. IIS and .Net then unpackage this and make the actual call against the web service as long as the web service supports the same Interface used on the client side with the proxy. The result from the web service is then returned back over the Internet and back out of the proxy through the Interface back to the client code that had started the call in the first place. So the steps go something like this...
-
Create a proxy object that represents a particular web service by URL
-
Cast the proxy object as a Interface
-
Call methods on the Interface which get forwarded to IIS
-
IIS and .Net create an object from the web service and casts it as the same Interface
-
The call is made against the Interface attached to the web service object
-
The return result from the call is sent back to the client

Most web developers have never dealt with Interfaces. I first started using interfaces when COM (Component Object Model) was introduced to Windows. An interface is simply a set of functions that go together in a group and are given a name such as IMyInterface. This name represents all of those functions together. It is an all or nothing thing. When you create an interface you do not really add any code, but instead you just create the "shell" of each function that defines what the name of each function is, what parameters to pass into each function, and what the function returns when it is done. So if an Interface has no code in its functions how does it do anything? Well the Interface by itself does not do anything. Instead after you create an Interface you then create a class that you intend on creating objects with and then make that class "support" the interface. What does "supporting" an interface mean? It means that the class will declare that it supports that interface and then it actually implements each and every one of the functions in the interface. Each interface function that the class implements must match exactly each function in the interface as far as the parameters that are passed, the name of the function must be the same, and the return type must be the same.
So why go through all the trouble to create these interfaces (that have no code in them) and then recreate all those same functions again in your class (with code in them this time)? Well the answer can be looked at like this... A physical real world interface we all deal with are electrical outlet interfaces. We have many different devices (classes/objects) that need to have electricity to work. But we only have one or two types of electrical outlets (interfaces) that are supported in each country of the world. So what we do is we decide to standardize all electrical devices to support a small number of interfaces that they can get electricity from. Then we can go to any house any where in our country and plug the electical devices (classes/objects) into an interface that gaurantees electricity. This way we dont have to have special electrical outlets for each and every device (classes/objects) that we might want to buy from the store. Now this metaphor goes even further. Some times you have a situation where the electrical device (classes/objects) cannot reach all the way to an electrical outlet. So what we do is we use a proxy for the electrical outlet that we call an extension cord. This extension cord is not the actual electical outlet, but it does know how to plug into one and it knows how to pretend it is an electrical outlet by supporting the same interface. We then can plug our Christmas tree into the electrical outlet proxy (extension cord) and now we get it all to work even though the Christmas tree and the electrical outlet are very far apart.
This works just the same way when dealing with objects that are hosted very far apart across the Internet. An object that is running on the client supports an interface that will allow it to talk to a database. Another object that is on a server on the Internet also supports this same interface. But they are too far apart and so what you do is you get out a proxy (extension cord) to act as a middle man to make the connection. So there are two benefits to using Interfaces. 1) is we can plug into many different types of objects that all support the same interface and 2) we can use proxies that give us extra flexibility to jump over distance gaps.
So when we are designing our cluster software we really need to start off thinking about what is the design of our interfaces. What things do we need to plug into the objects on the servers? Or more specifically what data do we need to move back and forth between the clients and the servers? We also need to decide where we are going to define these interfaces. When the client says it is using Interface1 then that MUST be the same Interface1 that is on the server. The easiest way to do this is to define a DLL file that contains only interfaces and then use this same DLL file on both the client PCs and the servers. Then any time you add a new interface or make changes to an interface you distribute the interfaces DLL to both the client and server so they can both plug into each other correctly.
Lets start off designing a very simple interface and a web service class that supports the interface. This web service is not going to do much work. We will just make it return a string that says "Hello Client. From Server.". Following is a small video showing me using Visual Studio 2005 to create the interface in its own DLL file and then a web service that will implement this interface.
Create an interface and a class that supports it.