To design successful Web Services applications, you need to follow the "golden rules" presented here. To explain these rules, we'll use a scenario of a Web Service making calls to another Web Service. We have the client connecting to Web Service (Pink), connecting to Web Service (Blue).
The golden rules are as follows:
The minFreeThreads, maxConnections, maxIOThreads, and maxWorkerThreads settings must be optimized, based on the application.
The connection timeout of the Web Service call must be less than the executionTimeout of the Pink tier.
If the Pink tier times out, the call to the Blue tier must be aborted.
Do not write Web methods that do work in a tight loop (applies to version 1.0 of the framework).
Do not call STA objects from Web Services.
We'll take a closer look at each of these rules in the following sections.
The following is the threading model for a Web Service making calls to another Web Service:
Thread 1[IO/Worker]: Request is made to the Pink tier .asmx, which in turn calls the Web Service on the Blue tier.
Thread 1[IO/Worker]: Sends a POST request and blocks on ws2_32!Send.
| Note |
With IIS 5, Thread 1 would most likely be an IO thread. With IIS 6, Thread 1 would be a worker thread. |
Thread 2[IO]: Receives HTTP 100 Continue via WSARecv.
Thread 3[Worker]: Sends SOAP envelope via WSASend.
Thread 2[IO]: Receives the result of Web Services via WSARecv.
Thread 1[IO/Worker]: Returns from ws2_32!Send.
| Note |
If security was used to call the Web Service on the Blue tier, there is the potential to use a fourth thread. |
Table 18-1 describes the Web Services settings and shows some simple formulas for determining the proper settings.
|
Setting |
Description |
Proper Setting |
|---|---|---|
|
maxConnections |
Sets the maximum concurrent outbound Web Service calls per IP address. |
Set to the number of simultaneous outbound connections to a specific IP address that you expect. Should be incremented in steps of 4 to determine optimum settings for application and hardware. |
|
minFreeThreads |
Preserves a set number of threads for outbound Web Service calls. In the example presented here, we use three threads for each Web Service call, and possibly one more if security is involved. |
maxConnections times 3 (use 4 if security is being used). |
|
minLocalFreeThreads |
Same as minFreeThreads, but focuses on requests that are outbound to the same server. | |
|
maxIOThreads |
Determines the total number of IO threads available in the thread pool that can service requests. |
Needs to be greater than maxConnections + minFreeThreads. |
|
maxWorkerThreads |
Determines the total number of worker threads available in the thread pool that can service requests. |
Needs to be greater than maxConnections + minFreeThreads. |
| Note |
When the number of threads running in the thread pool exceed the value of minFreeThreads, you will begin to queue inbound requests. |
To tune the thread pool performance, if your CPU utilization is low and you are queuing requests, you should increase the value of maxIOThreads and maxWorkerThreads . On the other hand, if you CPU utilization is high, you should lower these settings.
| Note |
When CPU utilization exceeds 95 percent, you do not create additional threads. When CPU utilization is below 80 percent, you free unused threads. |
The connection timeout setting for a Web Service proxy should always be less than the settings defined by HttpRuntime.executionTimeout . If you do not follow this pattern, you will leak socket connections.
You must also be aware of how time is spent in your methods. For example, if you have configured your executionTimeout on the Pink tier to 90 seconds and have also configured the Web Service proxy to 70 seconds, you can run into problems. Let's say the incoming request to the Pink tier consumes 50 seconds before it makes the call to the Blue tier. This would allow you approximately 20 seconds to run the Web Service call to the Blue tier.
The recommendation is to make sure that the executionTimeout setting is no less than the time it takes to get to your Web method call plus the proxy timeout. So in our example, if testing proved these numbers to be consistent, our executionTimeout should be no less than the value defined in testing to be the amount of time it takes to get to the Web Service call, plus the maximum amount of time it takes to execute the call.
To recap, given a call to a Web method on the Pink tier, determine the appropriate timeout setting as follows:
Define the time it takes from the beginning of the Web method in the Pink tier to the call to the Web method on the Blue tier. Let's call this A.
Define the maximum amount of time we are going to set for the Web Service proxy (Blue). Let's call this B.
Set the value of executionTimeout as greater than the value of A + B.
Use GetWebRequest to get the current request, as follows:
Private WebRequest -request;
Protected override WebRequest GetWebRequest(Uri uri)
{
_request = base.GetWebRequest(uri);
return _request;
}
Then do the following in the method that invokes the Web Service:
Public string SleepItOff(int sleepTime)
{
Bool timeout = true;
Try
{
Object[] results =
this.invoke("SleepItOff", new object[]{sleepTime});
Timeout = false;
Return ((string)(result[0]));
}
Finally
{
If(timeout && _request!=null)
_request.Abort();
}
}
| Note |
You must place the abort code in the finally block, because when executionTimeout is reached, you call ThreadAbortException, which can be caught in a catch block. |
The design in version 1.0 of the framework was that inbound requests were handled by I/O threads. By design, the IO completion port will allow only the number of threads executing to be equal to the concurrency value. If you have a Web method that enters a tight loop or takes a sufficiently long period of time to execute without entering a wait state, the scalability of your Web Service will be impacted.
If you need to use a Web method that works in a tight loop, you should queue the work to a worker thread, via the following call:
ThreadPool .QueueUserWorkItem(new WaitCallback (DoBackgroundWorkLong));
This will place the IO thread in a proper sleep, allowing for other IO threads to get execution time.
Accessing STA objects from a Web Service is not supported for the simple reason that STA objects provide automatic synchronization to all clients. There are some workarounds that can be found in the Microsoft Knowledge Base article "XML Web Services and Apartment Objects," which you can find at http://support.microsoft.com/?id=303375.