Introduction
Most of the articles about optimizing ASP.NET webservices cover the topic of optimizing only the server-side code (which is mostly running on a tough multi-processor machine with two gigs of memory and fast SCSI drives). But it should be remembered that client-side optimizations and network traffic optimizations also have to be applied, otherwise it may become a bottleneck. This article explains how to optimize client calls to webservices' methods.
1. Call Web Methods Asynchronously
First of all, invoke your web-methods asynchronously. Invoking something asynchronously means that an invoked member will perform its work on a different thread. And after all the work is done, this member must notify the main thread and, optionally, pass the return values to it. This seriously increases performance. If a complicated and slow web method is executed in a separate thread, the main app can proceed performing its tasks without waiting for a web method to complete.
There are a lot of articles on the internet covering this topic, so I'll give just a brief introduction. When VS.NET generates a proxy class for a web service, it automatically generates the methods Begin<webmethodname>
and End<webmethodname>
for each web method. This short example shows how to use them.
Assume we have a webservice MyWebService
and a web method int Sum(int a, int b)
which returns a+b
. The proxy class will contain two automatically generated methods:
public System.IAsyncResult BeginSum(int a, int b,
System.AsyncCallback callback, object asyncState)
{
return this.BeginInvoke("Sum", new object[] {a, b},
callback, asyncState);
}
public int EndSum(System.IAsyncResult asyncResult)
{
object[] results = this.EndInvoke(asyncResult);
return ((int)(results[0]));
}
And here is how we should call the Sum
web method asynchronously from our app:
private MyWebService webService = new MyWebService();
private int result = 0;
public void InvokeSum(int a, int b)
{
AsyncCallback asyncCallback =
new AsyncCallback(FinishSum);
webService.BeginSum(a, b, asyncCallback, null);
}
public void FinishSum(IAsyncResult iAsyncResult)
{
this.result = webService.EndSum(iAsyncResult);
}
2. Create instances in advance to preload all the referenced assemblies at the very beginning
Everybody notices that a first call to a webservice takes more time to execute than the subsequent calls. Why is that? Try to run your application in Debug mode and look at the "Output" window of Visual Studio. Once a web method is invoked, the application loads a number of libraries like System.Web.dll, System.Web.Services.dll, System.XML.dll etc. And maybe other assemblies of your own, which describe some classes you use to work with your webservice. This loading takes time, of course. So create an instance of a web service in advance, when your application starts. This loads all the needed libraries in memory and saves time.
3. Use pre-authentication
This section applies only to webservices that use some authentication mechanisms, other than "anonymous".
The class SoapHttpClientProtocol
is a base class for any webservice-proxy. This class has a PreAuthenticate
property. Here's a quote from MSDN which describes this property:
When PreAuthenticate
is true
, the WWW-authenticate header is sent with the first request if the authentication mechanism supports doing so. When PreAuthenticate
is false
, a request is made to the XML Web service method without initially attempting to authenticate the user. If the XML Web service allows anonymous access, then the XML Web service method is executed. If anonymous access is disallowed, a 401 HTTP return code is sent back to the client. In response, the WebClientProtocol
class returns the authentication credentials to the Web server. If the client is authenticated and subsequently authorized to access the XML Web service, the XML Web service method is executed; otherwise the client is denied access.
In other words: any call to a webservice's method generates at least one 401 error (and if you use "integrated Windows authentication", then there might be two or three 401 errors!). To avoid this, set PreAuthenticate
to true
.
But keep in mind that not all authentication methods used by the hosting web servers support pre-authentication. Again, see this MSDN article. If your hosting IIS-server uses "Windows Integrated Authentication", pre-authentication is not possible. To speed-up the web service call, use the "basic" authentication scheme. But remember, that "basic" is vulnerable. Use it only with SSL-protected webservices and websites.
4. Edit IIS custom error messages
When something goes wrong, a web server displays an error message. The htm-files containing these messages are located in the c:\WINDOWS\Help\iisHelp\common\ server folder (if not, look at your IIS settings). Edit these files to minimize traffic! Get rid of styles, tables, and other "beauties"!
You may ask, what for? As I motioned in section 3 (pre-authentication), there' always a 401 error when a web-service is accessed. Even if PreAuthenticate
is set to true
, the 401-error will be generated when a webservice is accessed for the first time. So minimize the 401-files.
For example, here's my 401-2.htm:
Error 401.2 - Unauthorized: Access is denied due to server configuration.
My 401-2.htm file is only 73 bytes long. And IIS's default 401-2.htm is 5 kilobytes (feel the difference).
5. Use Compression
Compress the XML-traffic between the client and the server using the SOAPExtensions mechanism and any compression algorithm. You can use a ZIP-compression for you data. There are a lot of open-source compression libraries for C#. For example, you can use a free library called SharpZipLib to compress your web methods. Read this article and download the "CompressionExtension" project. Adding a compression to your webservice is very easy: include the downloaded project into your solution, reference it in your webservice, and mark your web methods with [CompressionExtension]
in the proxy-class code. Also, add the [CompressionExtension]
flag in the .asmx component. That's it.