Your Ad Here

Saturday, January 2, 2010

web service dispatcher program


I love the work of developing simple, reusable, and hopefully long lasting software components. By simple I mean easy to use, not necessarily easy to design or implement. As software engineers we should, in my opinion, work hard to make complicated things look simple, not the other way around. On the other hand, I like distributed computing, which is pretty complicated by nature. I get very excited if my programs can jump from machine to machine, go through firewalls, and eventually retrieve or update data on a legacy system (which usually means mainframe to me :-).

At work we have a lot of existing programs designed initially for internal use. The trend is to move functionalities provided by these programs to web servers so that we can serve more customers over the internet. It is impractical to rewrite a lot of these programs using the latest and greatest technology. Typically we wrap the needed functionalities in various web methods and other programs will invoke these methods. Most of our new web server programs are developed using .NET. To invoke a web method from .NET code, according to the documentation, you need to add a web reference to your project. A proxy class for the web service will be generated for your project. You can also manually generate a proxy class using the wsdl.exe tool. The .NET generated proxy class is hard-coded for a specific web service. You cannot use the same proxy class to access a different web service. If your program needs to use 10 different web services, you will have to generate 10 different proxy classes and compile those classes into your code.

The .NET framework class SoapHttpClientProtocol is the base class for all generated web service proxies. I have tried without success to use this base class to build a reusable proxy class for general web services. Fortunately, I figured out my own solution by using the SoapClient object in the Microsoft Soap Toolkit. The SoapClient object "provides a client-side, high-level interface whose methods and properties send a Simple Object Access Protocol (SOAP) request to the server and process the response from the server". In order to invoke a web method in a most flexible way, I have constructed a com component which is a general web service proxy. My proxy is a key component in the web service dispatcher program and it is described in a separate article .

The web service dispatcher program

This program itself is a simple web service written in ASP.NET. It simplifies the work of building and running web client/server programs. Here is how it works. First, we register existing web methods on the web service dispatcher to make them available to other programs. If you have a program that needs to access multiple web methods implemented in different web services, instead of calling those methods individually, you can write the code to call my web service dispatcher providing only the name of the web method you want to call and the input arguments. The web service dispatcher will forward your request to the corresponding server program and also return the output to you. Again, your code need only to know the name, the input format, and the output format of the web method you want to invoke. So as long as the name, the input format, and the output format of the web method do not change, the server program that implements this particular web method can be changed or moved to a different physical location without affecting your existing code. In fact, the web method you want to invoke can be implemented in more than one server programs, some may be written in C++ and others in VB (or even in Java).

Here is a summary of the main advantages of using the web service dispatcher.

  • The programs that use various web methods need only to access the web service dispatcher program.
  • The server programs need not be implemented using .NET and they don't have to be running on the windows platform either. The same goes with client programs. The dispatcher itself is implemented using .NET, however.
  • Multiple server programs running on one or more machines can provide the same web method (uniquely identified by its name string) through the web service dispatcher so that the client programs can still function if one of the server programs crashes.
  • Multiple instances of the web service dispatcher can be used. The same server program can make its web methods available through multiple dispatchers.

In order to use the web service dispatcher, we need to do the following.

  1. Install and run an instance of the web service dispatcher program.
  2. Register existing web methods on the dispatcher.
  3. The other programs invoke the registered web methods through the dispatcher.

There are some restrictions on using the web service dispatcher. First, web methods registered with the dispatcher can only take string arguments and the return value must also be a string. This is to simplify the implementation of the web service dispatcher. Since it is easy to use XML to represent complicated data types, this restriction is really not a big deal. Secondly, a web method implementation must be uniquely identified by the registered method name. For example, if there are two different server programs each registers a web method called GetPersonInfo on the same instance of the dispatcher, then they must implement the same thing, you cannot have one implementation returns the birthday while the other returns the social security number. The dispatcher assumes that all implementations of the same method are the same and will randomly pick a server, if there is more than one, to process a user request.

Here are the main public methods of the web service dispatcher.

RegisterMethod. This method takes a single string argument and returns a boolean flag, true for success and false for failure. You can register multiple web methods with only one call to this method. We will cover this method in detail later.

UnRegisterMethod. This is, of course, used to unregister a web method. Details will be discussed later.

InvokeMethodX. Here X is a number ranging from 0 to 5. The first argument of this method is the name string of the web method you want to invoke. If the web method you want to invoke does not take any argument, then you use the InvokeMethod0 . Otherwise, you can provide the input arguments by using InvokeMethod1 , InvokeMethod2 , ..., and InvokeMethod5, depending on the number of arguments. The returned value of InvokeMethodX is the empty string if an error occurs, otherwise it is the output string of the web method you just invoked.

As you can see, we can use the web service dispatcher as some sort of "gateway" and let all other programs register and access web methods through this gateway. This will make the implementations of client and server programs easier and it is likely a more flexible and distributive design. Now consider scalability and reliability, what if there are too many users trying to access the web service dispatcher? Will it become a bottleneck? What if the machine that runs the gateway goes down? The answer is, we do not have to restrict ourselves to a single gateway.

For example, suppose we have 10 instances of server programs running on 10 different machines and each of them registers the same set of 15 web methods on 3 different instances of web service dispatcher. If there are 300 simultaneous users (or client programs) trying to call these 15 web methods, we could, in theory anyway, divide these 300 users into 3 groups of roughly the same workload, and let each group access a separate gateway. Please note that if one of the server instances crashes, most client programs will not be affected because the remaining 9 server instances are still providing the same set of web methods. The dispatcher program will randomly pick a server instance if multiple exist for the same web method. This example is pure imaginary and I have no resource to do any realistic testing.

How to install and use the web service dispatcher

The dispatcher has to be installed on a machine that already has the .NET framework and the Microsoft Soap Toolkit 3.0 (you can still use Microsoft Soap Toolkit 2.0 by modifying the web.config file).

Step 1. First, unzip the file WebServiceDispatcher.zip into a directory on the target machine, you need to preserve directory structure while unzipping.

Step 2. Create a virtual web directory WebServiceDispatcher pointing to the above directory.

Then we need to register existing web methods with the dispatcher.

Step 3. Call the RegisterMethod method of the dispatcher.

The RegisterMethod method does not have to be called by the server instance that implements the web method, but typically a server calls RegisterMethod to register its web methods when starting up. The following VB script registers a web method named GetData .

Collapse
Option Explicit

Const WSDL_URL = _
"http://localhost/WebServiceDispatcher/ServiceDispatcher.asmx?wsdl"
Const INPUT_XML = _
"_
GetData_
Server11_
http://Server11.com/MyService/MyService.wsdl_
"


Dim spclt
Set spclt = CreateObject("MSSOAP.SoapClient")
spclt.mssoapinit WSDL_URL

If spclt.RegisterMethod(INPUT_XML) Then
WScript.Echo "Web method registered"
Else
WScript.Echo "Failed to register web method"
End If
set spclt = nothing

Here is an example of the input string to the RegisterMethod method. As you can see, it is possible to register multiple web methods with one call.

Collapse
<Root>
<MethodList>
<Method>
<MethodName>GetName</MethodName>
<InternalMethodName>GetName1</InternalMethodName>
<ProviderName>NameProvider1</ProviderName>
<ServiceURL>http://NameProvider1.com/WebService/WebService.wsdl
</ServiceURL>
<ProxyServer>MyProxy.Com</ProxyServer>
<ProxyPort>91</ProxyPort>
<AuthUser>Tester</AuthUser>
<AuthPassword>123456</AuthPassword>
</Method>
<Method>
<MethodName>GetName</MethodName>
<InternalMethodName>GetName2</InternalMethodName>
<ProviderName>NameProvider2</ProviderName>
<ServiceURL>http://NameProvider2.com/WebService/WebService.wsdl
</ServiceURL>
<ProxyServer>MyProxy.Com</ProxyServer>
<ProxyPort>91</ProxyPort>
<AuthUser>Tester</AuthUser>
<AuthPassword>123456</AuthPassword>
<Method>
<Method>
<MethodName>GetAge</MethodName>
<ProviderName>AgeProvider</ProviderName>
<ServiceURL>http://AgeProvider.com/WebService/WebService.wsdl
</ServiceURL>
</Method>
</MethodList>
</Root>

The <MethodName> string identifies the web method registered on the dispatcher. The <ProviderName> string identifies a server instance that implements this web method. The <InternalMethodName> string is the name of the web method on the server instance that implements it, you can register the same web method from two different server instances (two different providers) as demonstrated in the above XML. It is assumed that <MethodName> and <InternalMethodName> are the same in case <InternalMethodName> is not provided. When a request to invoke a web method comes to the dispatcher, the dispatcher will randomly pick a provider if there is more than one. The <ServiceURL> string is used by the dispatcher to access the provider of a web method on behalf of the clients. The <ProxyServer> and the strings are optional, they are needed only if there is a firewall between the dispatcher and the provider of the web method. The <AuthUser> and the <AuthPassword> strings are also optional, they are needed only if access to the web method provider are restricted by the given user name and password.

It is ok to register a web method for the same provider more than once, the last registeration will override the previous ones. For example, if the <ServiceURL> string was wrong when it was first registered, you can correct the information by registering it again.

A server providing a web method through the dispatcher should call the UnRegisterMethod method before shutting down itself. The format of the input string for UnRegisterMethod is the same as that of the RegisterMethod except that you need only to specify <MethodName> and <ProviderName> . If you don't specify <ProviderName> , then the web method identified by <MethodName> will be unregistered for all providers!

Step 4. Other programs access the registered web methods by calling InvokeMethodX methods of the web service dispatcher.

Here is a VB script that calls the GetData web method registered in the above script, assuming it takes only one input argument.

Collapse
Option Explicit

Const WSDL_URL = _
"http://localhost/WebServiceDispatcher/ServiceDispatcher.asmx?wsdl"

Dim spclt
Set spclt = CreateObject("MSSOAP.SoapClient")
spclt.mssoapinit WSDL_URL

Dim output
output = spclt.InvokeMethod1("GetData", "This is an input string")

If output <> "" Then
WScript.Echo "Output: " & output
Else
WScript.Echo "Failed to invoke web meothod"
End If

set spclt = nothing

When writing real code, you should know what to send as the input arguments and how to process the output. I have included code for a sample web service called MathService which provides four web methods, Add, Subtract, Multiply, and Divide . To install MathService , you need to unzip the MathService.zip file into a directory on the target machine and create a virtual web directory named MathService for it. Run the script RegisterMathService.vbs to register methods of MathService on the dispatcher (assuming you already installed the dispatcher). Run the script InvokeMathService.vbs to invoke the methods of MathService through the dispatcher.

Other features of the web service dispatcher

The dispatcher program provides dynamic tracing capability. If you look at the web.config file of the dispatcher, you may find the following text.

Collapse
<appSettings>
<add key="TraceFilePrefix" value="Log\WebServiceDispatcherTrace" />
<add key="TraceLevel" value="40" />
<add key="TraceCleanup" value="7" />
<add key="SoapToolkitVersion" value="3.0" />
<add key="DataFile" value="Data\WebServiceDispatcherData" />
<add key="FailedAttemptLimit" value="3" />
</appSettings>

The value of TraceFilePrefix specifies where do you want to create trace files. If you set the value to c:\temp\Dispatcher , then trace files will be created in directory c:\temp and the file name will be the string Dispatcher plus a datetime stamp. By the way, a new trace file will be created for each day the dispatcher is being used so that you won't get a gigantic trace file if the dispatcher keeps running for months. The value of TraceLevel determines how much information you want in the trace file. Level 0 means no tracing will be done. Level 10 will generate error messages in the trace file. Level 20 means error plus warning messages. Level 30 will have additional messages indicating what internal functions have been called. Level 40 provides the most detailed tracing, which includes all input strings and output strings. You can dynamically invoke the SetTrace method on the dispatcher to change TraceFilePrefix and TraceLevel. In order to prevent the hard disk from filling up, old trace files will be cleaned up automatically after 7 days (you can modify this setting by changing the value of TraceCleanup in the web.config file).

Tracing for the dispatcher is implemented in the .NET component Tools.TraceUtility.dll. This component is ported from a C++ utility described in another codeproject article . The source code for this component is included in the TraceUtility.zip file.

Suppose you are using the web service dispatcher in a production environment. What happens if you have to restart IIS or reboot the machine? Do all server programs have to re-register their web methods with the dispatcher? The answer is no unless there is a real disaster (hard disk failure, blue screen of death, cannot shutdown IIS, etc.). This is because when the dispatcher program is shutdown normally, the registration information will be saved to a local file, and the information will be restored from the file when the dispatcher is restarted.

As stated earlier, before a server providing a web method through the dispatcher is shutdown, it should call UnRegisterMethod to clear the information about this provider stored on the dispatcher. What happens if two server instances register the same web method through the dispatcher and one of them becomes unavailable or dies unexpectedly? Will the dispatcher be smart enough to always forward user requests to the server instance that is still alive? The answer is no. The dispatcher just randomly picks a provider for the web method to forward the request, therefore some users may experience problems. However, the FailedAttemptLimit value in the web.config file determines how many times a web method provider can fail to respond to user requests. If the value is 10 , then the dispatcher will automatically unregister a provider when it failed 10 times consecutively. If the FailedAttemptLimit value is not specified in the web.config file, then the default value will be 3. So if there are five server instances providing the same web method through the dispatcher and four of them died a tragic death, then eventually all user requests for this web method will be processed by the one instance that is still alive!

Internal implementation of the dispatcher

The web service dispatcher program is implemented as a web service using ASP.NET. It consists of the files web.config, Global.asax , ServiceDispatcher.asmx, WebServiceDispatcher.dll , Tools.TraceUtility.dll , XYSoapClient.dll (and the .NET generated Interop.XYSOAPCLIENTLib.dll ).

The XYSoapClient.dll is a regular com dll built with Visual C++ 6.0 which implements the general proxy object for accessing various web services. It uses the SoapClient object from the Microsoft Soap Toolkit (either version 3.0 or 2.0). The reason I am using VC++ 6.0 instead of VC++ 7.0 to build this component is that I want to be able to use it on older platforms (machines without the .NET framework, etc.) and with older applications (VB 6.0 and VC++ 6.0 applications, etc.). The source code for this project is in the XYSoapClient.zip file. I am going to write a separate article to describe this component and give more C++ examples for accessing web services.

Update History

  • July 22, 2003: Modified article text. Added the capability to automatically delete old trace files. Modified the implementation of the dispatcher so that it is more efficient.
  • May 30, 2003: Updated source code for XYSoapClient.dll. The previous version does not handle array and reference types properly.
  • April 2, 2003: Updated source code and binary to set the soap client property ServerHTTPRequest to true. There seems to problems for some machines if this property is not set, however my server works fine without the additional code.

No comments:

Post a Comment