科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件ASPX页Web服务调用性能优化

ASPX页Web服务调用性能优化

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

本文介绍了如何通过异步方法消除使用ASP.NET Web服务调用的性能问题和线程池资源消耗问题

作者:佚名 来源:Microsoft 2007年11月8日

关键字: Windows

  • 评论
  • 分享微博
  • 分享邮件
异步 PreRequestHandler 执行

  ASP.NET 支持称为 HttpHandlers 的类。HttpHandlers 是实现 IHttpHandler 接口的类,用于为带有特定扩展名的文件的 HTTP 请求提供服务。例如,如果查看一下 Machine.config 文件,您将注意到,有许多 HttpHandlers 服务于带有扩展名(如 .asmx、.aspx、.ashx 甚至 .config)的文件的请求。对于带有特定扩展名的文件的请求,ASP.NET 将查看其配置信息,然后调用与其相关联的 HttpHandler 为该请求提供服务。

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

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

Public Function BeginPreRequestHandlerExecute(
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 MyProxy
proxy.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

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

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

Public Sub MyCallback(ByVal ar As IAsyncResult)
Dim proxy As MyProxy = ar.AsyncState
If proxy.Res.IsCompleted Then
proxy.Res.Callback.Invoke(proxy.Res)
End If
End Sub

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

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

Public Sub EndPreRequestHandlerExecute(ByVal ar As IAsyncResult)
If Request.Url.AbsolutePath _
= "/WebApp/PreRequestHandlerPage.aspx" Then
Dim res As MyAsyncResult = ar
Dim proxy As MyProxy = res.Proxy
Dim retString 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

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

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

  这不仅仅是理论 -- 它确实起作用!

  如果不考虑我没有阻塞了所有线程,至少也使得浪费的资源更少了,因而这还是有意义的。但实际的结果确实会有所不同吗?答案是肯定的“是”!我把此专栏中介绍的三种测试情况放在了一起:从 Web 页面代码进行 2 个阻塞的调用,从 Web 页面代码进行 2 个异步调用,以及从 PreRequestHandler 代码进行 2 个异步调用。我使用 Microsoft Application Center Test 对这三种情况进行了测试,在 60 秒钟内从 100 个虚拟客户端连续发送请求。下图显示的结果表明了在 60 秒钟内完成的请求数。


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

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


图 2:100 个同时进行请求的客户端的平均完成响应时间

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

  我必须指出,这些并非科学的数字是在我的并非科学的办公室中运行的并非科学的计算机上获得的。当然,如果将空闲的线程释放出来,让它们做一些实际的工作确实会改善性能,因而这也很有意义。希望这些结果能够表明性能的改善其实是非常显著的。

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

  小结

  无论何时遇到任何类型的进程耗时较长的性能问题,异步执行模型都是一个很好的方法。在从 .aspx 页面调用 Web 服务的情况下,我们认为可以将异步 Web 服务调用与 ASP.NET 提供的异步执行模式结合起来。这解决了在处理 .aspx 请求的过程中缺乏异步支持的问题。使用此异步方法可以消除性能问题以及线程池资源的消耗问题。

查看本文来源

    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章