唐山网站建设

设为主页 加入收藏 繁體中文

ASPX页Web服务调用性能优化

核心提示:ASPX页Web服务调用性能优化,浏览ASPX页Web服务调用性能优化,摘要:本文先容了如何通过异步方法消除使用MicrosoftASP.NET的Web服务调用的性能题目和线程池资源的消耗题目。情况:从ASP.NET页面调用Web服务时的性能破坏我们在本文中讨论Web服务时,期看在各种情况下都可以

摘要:本文先容了如何通过异步方法消除使用MicrosoftASP.NET的Web服务调用的性能题目和线程池资源的消耗题目。

情况:从ASP.NET页面调用Web服务时的性能破坏

我们在本文中讨论Web服务时,期看在各种情况下都可以享用Web服务。1个主要的情况是从中间层环境(如ASP.NETWeb页面)访问Web服务。为MapPoint.NETWeb服务的用户提供支持的职员常常收到这样的题目,即用户在使用其Web服务时,对MapPoint.NET的调用可能需要相当长的时间。这本身其实不是甚么题目,但某些其他因素可使之成为比表面上要严重很多的大题目。

HTTP双连接限制

HTTP规范表明,1个HTTP客户端与任1服务器最多可以同时建立两个TCP连接。这可以避免单个浏览器在浏览某个页面(例如,具有120个嵌进的缩略图)时,由于连接要求过量而使服务器负载太重。此时,浏览器将仅创建2个连接,然后通过这两个管道开始发送120个HTTP要求,而不是创建120个TCP连接并通过每个连接来发送HTTP要求。对中间层,此方法的题目在于,中间层可能会有50个同时要求连接的用户。假设不能不为每个用户进行1次MapPoint.NETWeb服务调用,将会有48个用户等待两个管道中的1个空闲下来。

线程池限制

ASP.NET处理传进的要求的方式是通过1个称为进程线程池的1组线程为其提供服务。正常情况下,要求传进后,池中某个空闲的线程将为其提供服务。这里的题目在于,进程线程池不会创建无数个线程来处理大量的要求。具有最大线程数限制是1件好事,由于假设我们无穷地创建线程,计算机上的全部资源将只能用来治理这些线程了。通过限制所能创建的线程数,我们可以把线程治理的系统开消保持在1个可控的水平。假设某个要求传进时线程池中的所有线程都被占用,则该要求将排队等候,在忙线程完成任务后,空闲出来的线程才能处理新要求。此方法实际上比切换到某个新线程更有效,由于不需要在要求之间进行线程切换。但存在的题目是,假设线程的使用效率不高(特别是在非常忙的Web服务器上),则等候的要求队列会变得很大。

考虑1下从ASP.NET页面进行Web服务调用的情况。假设进行同步调用,则正在运行的线程将被阻塞,直到Web服务调用完成为止。在调用期间,线程没法进行任何其他活动。它没法处理其他要求,只能等待。假设某个单处理器计算机上具有默许的工作线程数20,则只需20个同时进行的要求即可用完全部线程,以后的要求必须排队等候。

该题目不但限于Web服务

不但调用Web服务的用户会碰到从Web页面进行调用时的拥堵且耗时较长的题目。进行任意数目的较长的调用都会碰到一样的题目,例如:SQLServer?要求、长文件的读取或写进、各种Web要求或访问某个并发资源(其中锁定会造成严重的延迟)。实际上,有很多使用Web服务的情况,其服务调用比较迅速,其实不是甚么题目。但您或许会理解,假设您想通过代理服务器调用MapPoint.NETWeb服务,所使用的连接具有1定的延迟,同时相应的服务可能又要花费1些时间来处理要求,则您可能在各处位置都看到延迟的情况,并且假设站点很忙,即可能出现题目。

改良题目
 
该题目的某些方面可以通过对环境进行某些配置设置来改良。我们看1下可用于改良该题目的某些配置设置。

maxconnections

连接到Web资源的默许双连接限制可以通过1个名为connectionManagement的配置元夙来控制。connectionManagement设置答应您添加要让其采取非默许连接限制的站点的名称。可以将以下内容添加到典型的Web.config文件中,将您连接的所有服务器的连接限制默许值增加到40。

<configuration>
<system.net>
<connectionManagement>
<addaddress="*"maxconnection="40"/>
</connectionManagement>
</system.net>

<system.web>
...

应当留意的是,对本地计算机的连接数目历来都没有限制,因此,假设是连接到本地主机,则此设置无效。

maxWorkerThreads和minFreeThreads

假设收到HTTP503毛病(“服务暂时过载”),则表明线程池中的线程已全部占用,并且要求队列也已超出最大值(appRequestQueueLimit的默许设置为100)。对IIS5.0安装,可以简单地增加线程池的大小。而对IIS6.0安装(与IIS5.0不兼容),这些设置将无效。

maxWorkerThreads和maxIoThreads分别控制工作线程数和处理新提交的ASP.NET要求的线程数。这些设置需要在您的Machine.config中进行配置,它们将影响您计算机上运行的所有Web利用程序。maxWorkerThreads是Machine.config中的processModel元素的1部份,并且您在查看后会发现,该设置的默许值为每个处理器20个线程。

minFreeThreads设置可以在Machine.config中进行配置,或在您的利用程序的Web.config文件中的httpRuntime元素下进行配置。该设置的作用是,当空闲的线程数低于所设置的限制时,将制止使用线程池中的线程来处理传进的HTTP要求。假设您需要某个进程线程池线程完成挂起的要求,这会很有用。假设所有的线程都被用来处理传进的HTTP要求,并且这些要求在等待另1个线程完成其处理,那末就会进进死锁状态。例如,假设您正在从ASP.NET利用程序进行对某个Web服务的异步Web服务调用,并且在等待回调函数完成该要求,就会出现这类情况。由于回调必须在进程线程池中的空闲线程上进行。假设查看1下您的Machine.config,将会留意到minFreeThreads设置的默许值为8,假设工作线程池的限制为20,则该默许值还可以满足需要,但是,假设线程池的大小增加到100,该默许值就太小了。

应当留意的是,假设您的ASP.NET利用程序对本地计算机进行Web服务调用,则线程池限制的题目将被激化。例如,我为此专栏创建的测试利用程序调用与ASPX页面同处1台计算机上的Web服务。因此,对阻塞的调用,1个线程被同时用于ASPX页面和ASMXWeb服务要求。这有效地使Web服务器处理的同时要求数增加了1倍。在同时进行两个Web服务要求(使用异步Web服务调用)的情况下,我们终极使同时进行的要求数增加了两倍。为避免在回调本地计算机时出现此类题目,您应当考虑您的利用程序的体系结构,使其简单地直接从ASPX代码来履行Web方法中的代码。

WindowsXP限制

我们必须要留意,假设您在1个Windows?XP计算机上进行某项测试,则所面临的另1个限制是XPWeb服务器对所答应的同时连接数的人为限制。由于WindowsXP不是服务器平台,其同时连接数被限制为10。这对开发环境中的测试通常没题目,但是假设试图进行任何复杂的测试,该限制题目就会比较严重。本地计算机的连接不受此限制影响。

真实的解决方案:异步要求处理

调剂配置设置是1种改良题目的方法,而在实际设计Web利用程序时通过某种方式完全解决题目则是另1回事。等待阻塞的调用完成的线程永久也不会有更好的调剂余地,因此,解决的办法是完全避免阻塞题目。异步处理要求就是1个适当的解决方案。这表现在两个方面:进行异步Web服务调用,和在ASP.NETWeb利用程序中异步处理要求。

异步Web服务调用

在之前的专栏中,我写了有关异步调用Web服务的题目。能够使线程不用等待Web服务调用完成是创建开释线程以便处理更多要求的异步页面处理模型的关键部份。另外,异步调用Web服务也比较简单。

请考虑以下ASPX页面的VisualBasic.NET代码:

'错用同步Web服务调用所酿成的性能极差的
'页面!
PublicClassSyncPage
InheritsSystem.Web.UI.Page

ProtectedWithEventsLabel1AsSystem.Web.UI.WebControls.Label
ProtectedWithEventsLabel2AsSystem.Web.UI.WebControls.Label

PrivateSubPage_Load(ByValsenderAsSystem.Object,_
ByValeAsSystem.EventArgs)HandlesMyBase.Load
'调用Web服务
DimproxyAsNewlocalhost.Service1
Label1.Text=proxy.Method1(500)
Label2.Text=proxy.Method1(200)
EndSub

EndClass

此代码非常易懂。页面加载时将创建1个Web服务代理实例,然后用该实例两次调用1个名为Method1的Web方法。Method1只返回包括传递给该方法的输进参数的字符串。为了向该系统添加1定程度的延迟,Method1在返回字符串之前还休眠了3秒钟。从调用返回到Method1的字符串被放在ASPX页面上的两个标签的文本中。该页面提供的性能极差,并且像1块海绵1样从进程线程池中吸取线程。由于在Method1Web方法中有3秒钟的延迟,对该页面的1个调用最少要6秒钟才能完成。

以下代码片断显示了1个类似Web页面的代码,只不过现在进行的是异步Web服务调用。

PublicClassAsyncPage
InheritsSystem.Web.UI.Page

ProtectedWithEventsLabel1AsSystem.Web.UI.WebControls.Label
ProtectedWithEventsLabel2AsSystem.Web.UI.WebControls.Label

PrivateSubPage_Load(ByValsenderAsSystem.Object,_
ByValeAsSystem.EventArgs)HandlesMyBase.Load
'调用Web服务
DimproxyAsNewlocalhost.Service1
DimresAsIAsyncResult
=proxy.BeginMethod1(500,Nothing,Nothing)
Dimres2AsIAsyncResult
=proxy.BeginMethod1(200,Nothing,Nothing)
Label1.Text=proxy.EndMethod1(res)
Label2.Text=proxy.EndMethod1(res2)
EndSub

EndClass

一样,该页面将创建1个Web服务代理,然后两次调用Method1Web方法。不同的是,现在调用的是BeginMethod1,而不是直接调用Method1。BeginMethod1调用将立即返回,这样我们便可以够开始第2次调用该方法。与第1个示例中等待第1个Web服务调用完成不同,现在我们可以同时开始这两个调用。对EndMethod1的调用只是在特定的调用完成前会造成阻塞。

值得留意的是,当我们从ASPX页面返回后,响应将发送给客户端。因此,在取得所需的数据之前,我们没法从Page_Load方法返回。这就是我们要阻塞Web服务调用直至其完成的缘由。好的方面是两个调用可以同时履行,因此先前6秒钟的延迟现在将降到3秒钟左右。这固然好1些,但依然创建了阻塞的线程。我们真正需要的是在完成Web服务调用的同时,能够开释线程以便其处理HTTP要求。题目在于,ASPX页面的处理模型没有1个异步履行模式。不过,ASP.NET确切提供了1个解决此题目的方法。

异步PreRequestHandler履行

ASP.NET支持称为HttpHandlers的类。HttpHandlers是实现IHttpHandler接口的类,用于为带有特定扩大名的文件的HTTP要求提供服务。例如,假设查看1下Machine.config文件,您将留意到,有很多HttpHandlers服务于带有扩大名(如.asmx、.aspx、.ashx乃至.config)的文件的要求。对带有特定扩大名的文件的要求,ASP.NET将查看其配置信息,然后调用与其相干联的HttpHandler为该要求提供服务。

ASP.NET还支持写事件处理程序,在处理Http要求进程中的各个时候都可以产生这类事件。其中1个事件是PreRequestHandlerExecute事件,它恰好产生在某个特定要求的HttpHandler被调用之前。还有1个对PreRequestHandlerExecute通知的异步支持,可以注册这些通知以使用HttpApplication类的AddOnPreRequestHandlerExecuteAsync方法。HttpApplication类源自基于Global.asax文件创建的事件处理程序。我们将使用异步PreRequestHandler选项为Web服务调用提供异步履行模式。

在调用AddOnPreRequestHandlerExecuteAsync之前要做的第1件事是创建1个BeginEventHandler和1个EndEventHandler函数。要求传进后,将调用BeginEventHandler函数。我们将在此时开始异步Web服务调用。BeginEventHandler必须返回1个IAsyncResult接口。假设您正在进行1个Web服务调用,则可以只返回由Web服务begin函数返回的IAsyncResult接口(在我们的示例中,将由BeginMethod1方法返回1个IAsyncResult接口)。在我创建的示例中,我想履行与前面的Web页面示例(其中揭露了同步和异步Web服务调用)相同的操纵。这就意味着我必须创建自己的IAsyncResult接口。我的BeginEventHandler代码以下所示:

PublicFunctionBeginPreRequestHandlerExecute(
ByValsenderAsObject,_
ByValeAsEventArgs,_
ByValcbAsAsyncCallback,_
ByValextraDataAsObject)AsIAsyncResult
IfRequest.Url.AbsolutePath_
="/WebApp/PreRequestHandlerPage.aspx"Then
DimproxyAsMyProxy=NewMyProxy
proxy.Res=NewMyAsyncResult
proxy.Res.result1
=proxy.BeginMethod1(_
500,_
NewAsyncCallback(AddressOfMyCallback),_
proxy)
proxy.Res.result2
=proxy.BeginMethod1(_
300,_
NewAsyncCallback(AddressOfMyCallback),_
proxy)
proxy.Res.Callback=cb
proxy.Res.State=extraData
proxy.Res.Proxy=proxy
Returnproxy.Res
EndIf
ReturnNewMyAsyncResult
EndFunction

关于此代码还有很多有趣的事情值得留意。首先,针对此虚拟目录处理的每个HTTP要求都将调用此代码。因此,我做的第1件事就是检查要求的实际路径,查看它是否是是我要为其提供服务的页面的路径。

我的函数使用了1些有趣的输进参数来调用。cb参数是ASP.NET传递给我的回调函数。ASP.NET希看在我的异步工作完成后,可以调用由它提供给我的回调函数。它们就是通过这类方式知道甚么时候调用我的EndEventHandler。一样,假设我只进行1个Web服务调用,则只需将回调传递给BeginMethod1调用,然后Web服务调用将负责调用函数。但在本例中,我进行了两个单独的调用。因此,我创建了1个传递给两个BeginMethod1调用的中间回调函数,并且在回调代码中检查两个调用是否是都已完成。假设没完成,我将返回;假设已完成,我将调用原始的回调。另1个有趣的参数是extraData参数,它在ASP.NET调用我时为ASP.NET保存了状态。我在调用由cb参数指定的回调函数时必须返回该状态信息,因此,我将其存储在所创建的IAsyncResult类中。我的回调代码以下所示:

PublicSubMyCallback(ByValarAsIAsyncResult)
DimproxyAsMyProxy=ar.AsyncState
Ifproxy.Res.IsCompletedThen
proxy.Res.Callback.Invoke(proxy.Res)
EndIf
EndSub

还应当提到的1点是,我创建的实现IAsyncResult的类(称为MyAsyncResult)将在查询IsCompleted属性时检查两个挂起Web服务调用的完成情况。

在EndEventHandler中,我只是从Web服务调用获得结果,然后将其存储在当前的要求上下文中。该上下文与要传递给HttpHandler的上下文相同。在本例中,它是.aspx要求的处理程序,这样它即可以用于我的标准代码。我的EndEventHandler代码以下所示:

PublicSubEndPreRequestHandlerExecute(ByValarAsIAsyncResult)
IfRequest.Url.AbsolutePath_
="/WebApp/PreRequestHandlerPage.aspx"Then
DimresAsMyAsyncResult=ar
DimproxyAsMyProxy=res.Proxy
DimretStringAsString
retString=proxy.EndMethod1(proxy.Res.result1)
Context.Items.Add("WebServiceResult1",retString)
retString=proxy.EndMethod1(proxy.Res.result2)
Context.Items.Add("WebServiceResult2",retString)
EndIf
EndSub

由于已接收了.aspx页面的数据,因此实际的页面处理也就非常简单了。

PublicClassPreRequestHandlerPage
InheritsSystem.Web.UI.Page

ProtectedWithEventsLabel1AsSystem.Web.UI.WebControls.Label
ProtectedWithEventsLabel2AsSystem.Web.UI.WebControls.Label

PrivateSubPage_Load(ByValsenderAsSystem.Object,_
ByValeAsSystem.EventArgs)HandlesMyBase.Load

Label1.Text=Context.Items("WebServiceResult1")
Label2.Text=Context.Items("WebServiceResult2")
EndSub
EndClass

这不单单是理论--它确切起作用!

假设不考虑我没有阻塞了所有线程,最少也使得浪费的资源更少了,因此这还是故意义的。但实际的结果确切会有所不同吗?答案是肯定的“是”!我把此专栏中先容的3种测试情况放在了1起:从Web页面代码进行2个阻塞的调用,从Web页面代码进行2个异步调用,和从PreRequestHandler代码进行2个异步调用。我使用MicrosoftApplicationCenterTest对这3种情况进行了测试,在60秒钟内从100个虚拟客户端连续发送要求。下图显示的结果表明了在60秒钟内完成的要求数。


图1:100个同时进行要求的客户端在60秒钟内完成的要求

异步PreRequestHandler方法处理的要求数大约是排在第2位的方法处理的要求数的8倍。因此,该方法使您可以处理更多要求,但是对单个要求,实际要多长时间才能完成呢?下图显示了这3种方法的均匀响应时间。


图2:100个同时进行要求的客户真个均匀完成响应时间

使用PreRequestHandler方法的均匀要求响应时间仅为3.2秒。假定每个Web服务调用的内置延迟为3秒钟,则该方法是1种非常有效的解决办法。

我必须指出,这些并非科学的数字是在我的并非科学的办公室中运行的并非科学的计算机上取得的。固然,假设将空闲的线程开释出来,让它们做1些实际的工作确切会改良性能,因此这也很故意义。希看这些结果能够表明性能的改良实在是非常明显的。

PreRequestHandler方法是很必要的,由于.aspx要求的处理程序中没有内置异步要求处理机制。但并非所有ASP.NETHTTP处理程序都是这样。PreRequestHandler方法适用于所有ASP.NET要求类型,但使用将异步支持置于.asmx处理程序内的编程方式要比使用PreRequestHandler编程方式更轻易1些。

小结

不管甚么时候碰到任何类型的进程耗时较长的性能题目,异步履行模型都是1个很好的方法。在从.aspx页面调用Web服务的情况下,我们以为可以将异步Web服务调用与ASP.NET提供的异步履行模式结合起来。这解决了在处理.aspx要求的进程中缺少异步支持的题目。使用此异步方法可以消除性能题目和线程池资源的消耗题目。

下载本文相干源代码

http://www.fw8.net/


TAG:方法,代码,题目,页面,线程
评论加载中...
内容:
评论者: 验证码: