Asynchronous calls to Web services

17.4 Asynchronous calls to Web services

If the same Web service were deployed on several geographically separated Web servers, clients could connect to several Web services at once in order to improve performance. This may only be applicable in situations where several calls have to be made and each call takes a significant amount of time to complete.

To understand the scenario, we could envisage a situation where an application displays live stock values of a large share portfolio. A Web ser- vice is hosted on a server in the United States, which is linked into the NASDAQ exchange, and another server is located in Japan, which is linked into the Nikeii exchange. A customer in question has shares in Microsoft and Toyota. If the client were to issue a request for the value of the Microsoft shares, wait for the response, and then request the value of the Toyota shares, the process would take twice as long as if both requests were made simultaneously.

Several techniques can be used to manage simultaneous Web service calls. The following code examples perform the same function: They make two calls to a Web service and measure the response times to the calls. IIS is multithreaded, so it handles both of these requests in parallel. In a real- world example, the same Web service would be mirrored on more than one server, so that the two requests would be handled at exactly the same time.

Chapter 17

490 17.4 Asynchronous calls to Web services

Each of the following samples requires a simple user interface consist- ing of only a button and a label. To create this interface, open a new project in Visual Studio .NET, and select a Windows form application. Draw a button on the form and name it btnMakeCall and then draw a

label named lblStatus . You will also require a Web reference to the Web service as described ear-

lier in this chapter. This Web reference should be named localhost , for the purposes of these code examples. The Web service does not necessarily need to be hosted on the local machine.

17.4.1 Wait handles

A wait handle is equivalent to a do-nothing while loop using polling, but it is less processor intensive. This should only be used in a separate thread, or the client application will be nonresponsive to the user. This technique should only be used when useful client-side processing can be performed before data is returned from any of the Web services.

Click on the Make Call button and enter the following code: C#

private void btnMakeCall_Click(object sender, System.EventArgs e)

{ long timeStart = DateTime.UtcNow.Ticks; localhost.Service1 svc = new localhost.Service1(); IAsyncResult result1; IAsyncResult result2; result1 = svc.BegingetServerVariableNames(null,null); result2 = svc.BegingetServerVariable("REMOTE_ADDR",null,null); result1.AsyncWaitHandle.WaitOne(); result2.AsyncWaitHandle.WaitOne(); string[] varNames = svc.EndgetServerVariableNames(result1); string[] response = svc.EndgetServerVariable(result2); lblStatus.Text = "Time elapsed:" + (DateTime.UtcNow.Ticks - timeStart); lblStatus.Text += " ticks";

VB.NET

Private Sub btnMakeCall_Click(ByVal sender As Object, _

17.4 Asynchronous calls to Web services 491

ByVal e As System.EventArgs) Dim timeStart As Long = DateTime.UtcNow.Ticks Dim svc As localhost.Service1 = New localhost.Service1() Dim result1 As IAsyncResult Dim result2 As IAsyncResult result1 = svc.BegingetServerVariableNames( _

Nothing,Nothing) result2 = _ svc.BegingetServerVariable( _ "REMOTE_ADDR",Nothing,Nothing)

result1.AsyncWaitHandle.WaitOne() result2.AsyncWaitHandle.WaitOne() Dim varNames() As String = _

svc.EndgetServerVariableNames(result1) Dim response() As String = _ svc.EndgetServerVariable(result2) lblStatus.Text = "Time elapsed:" & _ (DateTime.UtcNow.Ticks - timeStart) lblStatus.Text += " ticks" End Sub

To test this code, run the application from Visual Studio .NET, and press the make Call Button. The user interface will become unresponsive until the call is received. In a production environment, the code detailed above should be contained within a separate thread.

17.4.2 Callbacks

Callbacks produce the least amount of processor overhead while waiting for Web service calls to return. They are ideal in situations where no useful cli- ent-side processing can be performed before all of the data is received; how- ever, it could be difficult to determine when the last call has returned successfully or erroneously.

Click on the Make Call button and enter the following code:

C#

public localhost.Service1 svc; public long timeStart; private void btnMakeCall_Click(object sender,

System.EventArgs e) {

Chapter 17

492 17.4 Asynchronous calls to Web services

timeStart = DateTime.UtcNow.Ticks; svc = new localhost.Service1(); svc.BegingetServerVariableNames(new AsyncCallback(ServiceCallback1),null); svc.BegingetServerVariable("REMOTE_ADDR",new AsyncCallback(ServiceCallback2),null);

} private void ServiceCallback1(IAsyncResult result)

{ string[] response = svc.EndgetServerVariableNames(result);

lblStatus.Text = "Time elapsed:" + (DateTime.UtcNow.Ticks - timeStart); lblStatus.Text += " ticks";

} private void ServiceCallback2(IAsyncResult result)

{ string[] response = svc.EndgetServerVariable(result); lblStatus.Text = "Time elapsed:" + (DateTime.UtcNow.Ticks - timeStart); lblStatus.Text += " ticks";

VB.NET

Public svc As localhost.Service1 Public timeStart As Long Private Sub btnMakeCall_Click(ByVal sender As Object, _

ByVal e As System.EventArgs) timeStart = DateTime.UtcNow.Ticks svc = New localhost.Service1() svc.BegingetServerVariableNames(New _

AsyncCallback(AddressOf ServiceCallback1),Nothing) svc.BegingetServerVariable("REMOTE_ADDR",New _ AsyncCallback(AddressOf ServiceCallback2),Nothing) End Sub Private Sub ServiceCallback1(ByVal result As IAsyncResult)

Dim response() As String = _

svc.EndgetServerVariableNames(result) lblStatus.Text = "Time elapsed:" & _

17.5 Interoperability 493

(DateTime.UtcNow.Ticks - timeStart) lblStatus.Text += " ticks"

End Sub

Private Sub ServiceCallback2(ByVal result As IAsyncResult) Dim response() As String = _ svc.EndgetServerVariable(result) lblStatus.Text = "Time elapsed:" & _ (DateTime.UtcNow.Ticks - timeStart) lblStatus.Text += " ticks" End Sub

To test this code, run the application from Visual Studio .NET, and press the Make Call button. The time displayed is the time that has elapsed between the issuing of the Web methods and the last response received. A more robust solution would be to use a global array that would track the progress of each call.

The BeginGetServerVariableNames function takes two parameters; the first parameter indicates the procedure to be called once the web-method returns, and the second, as shown in the code example above, is set to null or Nothing . This parameter can optionally contain objects that can be passed to the callback via the result object.