Matt PowellMicrosoft Corporation
July 22, 2003
Summary: Matt Powell describes how to eliminate performance issues that use Microsoft ASP.NET's Web Services Call with asynchronous methods and consumption issues of thread pool resources. (This article contains some links to English sites.)
Download the related sample code for this column.
In Microsoft, there is some interesting changes in the creation of Web content. As you may think, a lot of technical information you see on Microsoft.com requires participation from many people from Microsoft's different departments. Recently, those responsible for creating these contents, such as from product document groups, Microsoft Product Support Services (Most KnowledgeBases are generated by this), MSDN, and other groups, are reconsidering their content range. PSS first proposes this common problem, that is, the performance problem when the web service call is called from the ASP.NET web page. We feel that the problem beyond the range of KnowledgeBase articles, but it is not enough to form a product document.
However, it is a good topic for the "At Your Service" column.
Situation: Performance destruction when calling Web services from ASP.NET Page
When we discuss Web services in this column, we hope to enjoy Web services in a variety of situations. A main situation is to access Web services from an intermediate layer environment (such as ASP.NET Web Pages). Users who provide support for Mappoint .NET Web services often receive such problems, that is, when the user uses its web service, the call to Mappoint .NET may require a considerable time. This is not a problem, but some other factors can make it a big problem than the surface.
HTTP dual connection limit
The HTTP specification indicates that an HTTP client and any server can also establish two TCP connections at the same time. This prevents a single browser from browsing a page (eg, with a thumbnail with 120 embedded thumbnails), the server load is overweight due to excessive connection requests. At this point, the browser will only create 2 connections, and then send 120 HTTP requests through both pipes, rather than create 120 TCP connections and send HTTP requests through each connection. For intermediate layers, the problem is that the intermediate layer may have 50 simultaneous requests to connect. If you have to make a mappoint .NET web service call for each user, 48 users will wait for an idle in both pipes.
Thread pool limit
The way ASP.NET handles incoming requests is to provide services to a set of threads called process thread pools. Normally, after the request is incorporated, an idle thread in the pool will serve it. The problem here is that the process thread pool does not create countless threads to handle a large number of requests. With the maximum thread limit is a good thing, because if we create threads unlimited, all resources on your computer will only be used to manage these threads. By limiting the number of threads that can be created, we can keep the system overhead of thread management in a controlled level. If a request is incompatible, all threads in the thread pool are occupied, the request will wait in line. After the task is busy, the thread idle can handle new requests. This method is actually more efficient than switched to a new thread because thread switching is not required between the request. But the problem is that if the use of threads is not high (especially on very busy web servers), the waiting request queue will become large.
Consider the case where web service calls are performed from the ASP.NET page. If synchronous calls, the running thread will be blocked until the web service call is completed. Threads cannot make any other activities during the call. It can't handle other requests, just waiting. If a single processor computer has a default working thread number 20, only 20 simultaneous requests can be used with full thread, and the request must be queued. This problem is not limited to web services
Users who do not only call Web services will encounter congestion and time-consuming problems when they are called from the web page. Taking any number of longer calls will encounter the same problem, such as: SQL ServerTM request, long file read or write, various web requests or access a concurrent resource (where lock will cause serious delay) . In fact, there are many cases of using web services, and their service calls are relatively rapid, and what is not a problem. But you may understand if you want to call the Mappoint .NET web service through the proxy server, the connection used has a certain delay, and the corresponding service may take some time to handle the request, you may be in all positions. Seeing the latency, and if the site is very busy, there may be a problem.
Improving problem
Certain aspects of this issue can be improved by performing certain configuration settings for the environment. Let's take a look at some configuration settings that can be used to improve this issue.
MaxConnections
Default Dual Connection Limits connected to the web resource can be controlled by a configuration element named ConnectionManagement. The ConnectionManagement setting allows you to add a name to allow it to use a non-default connection restriction. You can add the following to a typical web.config file, adding the connection limits the connection limit of all servers you connect to 40.
connectionManagement>
...
It should be noted that the number of connections to the local computer has never been limited, so this setting is invalid if it is connected to the local host.
MaxWorkerthreads and Minfreethreads
If you receive an HTTP 503 error ("Service Temporary Overload"), it indicates that the thread in the thread pool is all occupied, and the request queue has also exceeded the maximum (AppRequestQueELIMIT's default setting to 100). For IIS 5.0 installation, you can simply increase the size of the thread pool. These settings will be invalid for IIS 6.0 installation (not compatible with IIS 5.0).
MaxWorkeRThreads and Maxiothreads respectively control the number of working threads and process the number of threads requested by the newly submitted ASP.NET. These settings need to be configured in your Machine.config, which will affect all web applications running on your computer. MaxWorkeRThreads is part of the ProcessModel element in Machine.config, and you will find that the default value of this setting is 20 threads for each processor.
Minfreethreads Settings can be configured in Machine.config or configured under the httpruntime element in your application's web.config file. The setting of this setting is that when the number of idle threads is lower than the set limit, the thread in the thread pool is disabled to process the incoming HTTP request. This will be useful if you need a process thread pool thread to complete the hanging request. If all threads are used to process incoming HTTP requests, and these requests are waiting for another thread to complete their processing, then they will enter the deadlock state. For example, if you are calling from the ASP.NET application for an asynchronous web service for a web service, this will happen to wait for the callback function to complete the request. Because the callback must be on the idle thread in the process thread pool. If you check your machine.config, you will notice that the default value of the Minfreethreads set is 8. If the work thread pool is limited to 20, the default value can also meet the needs, however, if the size of the thread pool increases to 100, This default is too small. It should be noted that if your ASP.NET application is called for a web service to a local computer, the problem of thread pool restrictions will be integrated. For example, I have built a web service that is the same as the ASPX page created for this column. Thus, a thread is simultaneously used for the ASPX page and the ASMX web service request. This effectively increases the number of requests to the web server to double. In the case of simultaneous two web service requests (using asynchronous web service calls), we will eventually increase the number of requests at the same time. To avoid such problems when callbacks local computers, you should consider the architecture of your application, making it simply directly from the ASPX code to execute the code in the web method.
Windows XP Limit
We must pay attention to that if you perform a test on a Windows® XP computer, the other restrictions faced is that the XP web server is limited to the number of connected ports allowed. Because Windows XP is not a server platform, its connection is limited to 10. This is usually no problem in the development environment, but if you try to make any complex test, this restriction problem will be more serious. The connection of the local computer is not affected by this limitation.
Real solution: asynchronous request processing
Adjusting Configuration Settings is another way to improve issues, and the problem is completely solved in some way when the Web application is actually designing the web application. Waiting for the bundled call to complete the thread will never have better adjustments, so the solution is to completely avoid blocking problems. Asynchronous processing request is an appropriate solution. This is in two aspects: asynchronous web service calls, as well as asynchronous processing requests in the ASP.NET web application.
Part 1: Asynchronous Web Service Call
In the previous column, I wrote the issue of asynchronous calling Web services. It is possible to make the thread to wait for the Web service call completion to create a key section of the release thread to handle more requests. In addition, asynchronous calling web services is also relatively simple.
Consider the following ASPX page Visual Basic® .NET code:
'The performance caused by the synchronous web service call is extremely poor
'Page!
Public Class SyncPage
Inherits System.Web.ui.page
Protected Withevents Label1 As System.Web.ui.WebControls.label
Protected Withevents Label2 As System.Web.ui.WebControls.Labelprivate Sub Page_load (Byval Sender As System.Object, _
BYVAL E as system.eventargs) Handles mybase.load
'Calling web services
Dim proxy as new localhost.service1
Label1.text = proxy.method1 (500)
Label2.text = proxy.method1 (200)
End Sub
END CLASS
This code is very easy to understand. A web service proxy instance is created when the page is loaded, and then calls a Web method called Method1 twice. Method1 only returns a string containing input parameters passed to the method. In order to add a certain degree of delay to the system, Method1 sleeps for 3 seconds before returning a string. The string returned to Method1 from the call is placed in the text of the two labels on the ASPX page. This page provides extremely poor performance and draws threads from the process thread pool like a sponge. Since a call to the page is at least 6 seconds to be completed for at least 6 seconds in the Method1 web method.
The following code snippet shows a code similar to a web page, but now is the current asynchronous web service call.
Public Class AsyncPage
Inherits System.Web.ui.page
Protected Withevents Label1 As System.Web.ui.WebControls.label
Protected Withevents Label2 As System.Web.ui.WebControls.Label
Private sub page_load (byval sender as system.Object, _
BYVAL E as system.eventargs) Handles mybase.load
'Calling web services
Dim proxy as new localhost.service1
DIM RES as IasyncResult
= Proxy.BeginMethod1 (500, Nothing, Nothing)
Dim Res2 As IasyncRESULT
= Proxy.BeginMethod1 (200, Nothing, Nothing)
Label1.text = proxy.endmethod1 (res)
Label2.text = proxy.endMethod1 (RES2)
End Sub
END CLASS
Similarly, this page will create a web service agent and then call the Method1 web method twice. Different, it is now called BeGinMethod1, not directly called Method1. BeginMethod1 call will return immediately so that we can start calling the method for the second time. Waiting for the first Web service call in the first example, now we can start these two calls at the same time. The call to EndMethod1 is only blocked before the specific call is completed.
It is worth noting that when we return from the ASPX page, the response will be sent to the client. Therefore, we cannot return from the Page_Load method before obtaining the required data. This is why we want to block the web service call until it is completed. Good aspects is that two calls can be executed at the same time, so the previous 6 second delay will now be reduced to about 3 seconds. Although it is better, it still creates a blocking thread. What we really need is to release the thread while completing the web service call, so that it handles HTTP requests. The problem is that the processing model of the ASPX page does not have an asynchronous execution mode. However, ASP.NET does provide a way to solve this problem. Part 2 of asynchronous solution: Asynchronous PrerequestHandler
ASP.NET supports classes called httphandlers. Httphandlers is a class that implements the IHTTPHANDLER interface, which is used to provide services for an HTTP request with a specific extension. For example, if you look at the Machine.config file, you will notice that there are many HTTPHandlers services to request files with extensions (such as .asmx, .aspx, .ashx or even .config). For requests with specific extensions, ASP.NET will view their configuration information, and then call Httphandler associated with it to serve the request.
ASP.NET also supports writing event handlers that can happen such events at all times during the handling of the HTTP request. One of the events is the prerequestHandleRexecute event, which happens before the Httphandler called a particular request is called. There is also an asynchronous support for PrerequestHandleRexecute notifications, which can register these notifications to use the AddonPreRequestHandleRexecuteasync method for the HTTPApplication class. The HTTPApplication class originates from the event handler created by the global.asax file. We will use asynchronous PrerequestHandler options to provide asynchronous execution modes for web service calls.
The first thing you want to do before calling AddonPrequestHandleRexecuteasync is to create a BegineventHandler and an EndeventHandler function. The BegineventHandler function will be called after the request is incorporated. We will start asynchronous web service calls at this time. BegineventHandler must return an IASYNCRESULT interface. If you are performing a web service call, you can return only the IASYNCRESULT interface returned by the web service begin function (in our example, an IASYNCRESULT interface will be returned by the beginmethod1 method). In the example I created, I want to perform the same operation as the previous web page example (which reveals synchronous and asynchronous web service calls). This means that I have to create my own IASYNCRESULT interface. My BegineventHandler code is as follows:
Public Function BeginPrequestHandleRexecute (
Byval sender as object, _
Byval e as eventargs, _
Byval Cb as asynccallback, _
BYVAL EXTRADATA As Object) As IasyncResult
If Request.url.absolutePath_
= "/Webapp/prerequesthandlerpage.aspx" THEN
Dim proxy as myproxy = new myproxyproxy.res = new myasyncresult
Proxy.res.Result1
= proxy.beginmethod1 (_
500, _
New asyncCallback (Addressof mycallback), _
Proxy)
Proxy.res.Result2
= proxy.beginmethod1 (_
300, _
New asyncCallback (Addressof mycallback), _
Proxy)
Proxy.res.callback = CB
Proxy.res.State = EXTRADATA
Proxy.res.proxy = proxy
Return proxy.res
END IF
Return New MyasyncResult
END FUNCTION
There are still many interesting things about this code worth noting. First, each HTTP request for this virtual directory processing will call this code. Therefore, the first thing I did is to check the actual path of the request, check if it is the path I want to provide services to it.
My function uses some interesting input parameters to call. The CB parameter is the callback function passed to my callback function. ASP.NET hopes that after my asynchronous work is completed, you can call the callback function provided by it. They know when to call my EndeventHandler in this way. Similarly, if I only conduct a web service call, simply pass the callback to the BeginMethod1 call, then the web service call will be responsible for calling the function. But in this example, I conducted two separate calls. Therefore, I created a middle callback function passed to two beginMethod1 calls, and check if the two calls have been completed in the callback code. If you are not finished, I will return; if I have finished, I will call the original callback. Another interesting parameter is an Extradata parameter, which saves the status for ASP.NET when you call me when ASP.NET calls. I must return this status information when calling the callback function specified by the CB parameter, so I am stored in the IASYNCRESULT class created. My callback code is as follows:
Public Sub MyCallback (Byval Ar As IasyncRESULT)
Dim proxy as myproxy = ar.asyncstate
If proxy.res.iscompleted the
Proxy.res.callback.invoke (proxy.res)
END IF
End Sub
It should also be mentioned that the class I created IASYNCRESULT (called MyasyncResult) will check the completion of two suspended web service calls when querying the iScompleted property.
In EndeventHandler, I just call the acquisition result from the web service, and then store it in the current request context. This context is the same as the context to be passed to HTTPHandler. In this example, it is a .aspx request handler so that it can be used for my standard code. My EndEventHandler code is as follows:
Public Sub EndprequestHandleRexecute (Byval Ar As IasyncResult)
If Request.url.absolutePath_
= "/Webapp/prerequesthandlerpage.aspx" THEN
Dim res as myasyncresult = ar
Dim proxy as myproxy = res.proxydim return as string
Retstring = proxy.endmethod1 (proxy.res.result1)
Context.Items.Add ("WebServiceResult1", RetString)
Retstring = proxy.endmethod1 (proxy.res.result2)
Context.Items.Add ("WebServiceResult2", RetString)
END IF
End Sub
Since data has been received, the actual page processing is very simple.
Public Class PrerequestHandlerPage
Inherits System.Web.ui.page
Protected Withevents Label1 As System.Web.ui.WebControls.label
Protected Withevents Label2 As System.Web.ui.WebControls.Label
Private sub page_load (byval sender as system.Object, _
BYVAL E as system.eventargs) Handles mybase.load
Label1.text = context.Items ("WebServiceResult1")
Label2.text = context.items ("WebServiceResult2")
End Sub
END CLASS
This is not just theory - it does work!
If you don't consider that I have not blocked all threads, at least make the waste of resources less, so this is still meaningful. But does the actual result do not differ? The answer is a sure "Yes"! I put three test sites introduced in this column: 2 blocking calls from the web page code, 2 asynchronous calls from the web page code, and 2 asynchronous calls from the PrerequestHandler code. I have tested these three situations using Microsoft Application Center Test, which continuously sends a request from 100 virtual clients within 60 seconds. The results shown below showed the number of requests completed within 60 seconds.
Figure 1: Request for 100 simultaneous requests in 60 seconds
The number of request processing processed by the asynchronous PrerequestHandler method is 8 times that of the number of requests processed by the second bit. Therefore, this method allows you to handle more requests, but how long does it take to complete for a single request? The following figure shows the average response time of these three methods.
Figure 2: 100 Average completion response time for 100 simultaneous requests
The average request response time using the PrerequestHandler method is only 3.2 seconds. Suppose the built-in delay of each Web service call is 3 seconds, the method is a very effective solution.
I have to point out that these are not scientific numbers are obtained on my non-scientific computer. Of course, if you release the idle thread, let them do some practical work, it is really improving, so this is also very meaningful. I hope that these results can indicate that performance improvement is actually very significant.
The PrerequestHandler method is necessary because there is no built-in asynchronous request processing mechanism in the processor for .aspx request. But not all ASP.NET HTTP handles are like this. I have introduced the .asmx request handler for web services with asynchronous models in the previous column. The PrerequestHandler method is suitable for all ASP.NET request types, but uses programming methods to be placed asynchronously supported in the .asmx handle than programming how to use PrerequestHandler. summary
An asynchronous execution model is a good method whenever any type of process takes longer performance. In the case where the Web service is called from the .aspx page, we believe that the asynchronous Web service calls can be combined with the asynchronous execution mode provided by the ASP.NET. This solves the lack of asynchronous support during processing .ASPX requests. Use this asynchronous method to eliminate performance issues and consumption issues for thread pool resources.
AT your service
Matt Powell is the chief content plan for MSDN XML Web Services Developer Center. He wrote a lot of articles for MSDN and various magazines, as well as the "Running Microsoft Internet Information Server" published by Microsoft Press, and Matt also assisted to develop a developing SOAP Toolkit 1.0. In his WEB service research, Matt likes to play with his wife and children in the Jatsa area.