您的位置:首页 > 编程语言 > ASP

ASP.NET 2.0 异步页面原理浅析 [1]

2008-03-07 10:41 381 查看
原文:http://www.blogcn.com//User8/flier_lu/blog/27401974.html

与 ASP.NET 1.0 相比,ASP.NET 2.0 的各方面改进可以说是非常巨大的。但就其实现层面来说,最大的增强莫过于提供了对异步页面的支持。通过此机制,编写良好的页面可以将数据库、WebService 调用等慢速操作,对网站吞吐能力的影响降到最低,并极大的改善网站的平均页面响应速度。本文将从使用和实现两个层面,简单的剖析这一强大机制的原理,以便读者能够更好的应用这一机制。

对一个网页请求的生命周期来说,首先是 Web 服务器收到客户端 HTTP 请求,将请求转交给 ASP.NET 引擎;引擎将以流水线方式调用合适的 Web 应用程序和最终的页面进行处理;页面会根据请求内容,执行某些后台操作,如访问数据库、调用远程 WebService 等等;最终将结果以某种可视化形式,展示到最终用户的浏览器中。

而为了提高响应速度和吞吐量,现代的 Web 服务器往往会将 Web 应用程序和页面,放在一个缓冲池中备用,避免每次处理请求时重建环境。而请求到来时,Web 服务器会从一个系统线程池中获取临时线程,调用从 Web 应用程序和页面缓冲池中获取的处理实例,完成对请求的处理,并最终返回处理的结果。

咋一看这种机制非常完美,能够最大限度的重用系统资源,但实际上其中存在着很大的优化余地。

我们可以将一个页面请求的处理过程进一步细化为下面步骤:

1.Web 服务器接受请求并由引擎转发请求

2.页面处理请求,访问数据库、调用远程 WebService 等

3.页面将处理结果,以某种形式展现,如 HTML 表格等

4.Web 服务器将结果返回给最终客户的浏览器

其中第一步涉及到核心态网络驱动,需要频繁切换回用户态,以将请求转交给处理引擎。这里涉及到大量的核心态和用户态切换。为减少这个负担,IIS 6 开始提供了 http.sys 在核心态直接对大多数请求进行处理。这是 MS 在中间件一级就已经替我们做好的优化,我们无需也无法关心。

而另外一个潜在的优化点就是异步页面的目标:增强页面处理请求的并发性。

Web 服务器在从线程池获取临时线程后,在线程中调用页面相关代码处理请求。而这里的请求处理过程往往涉及到较为缓慢的操作,例如访问数据库、调用远程 WebService 等。如果数据库是在本机的话还好,系统 CPU 时间只是从处理线程移交到后台数据库线程;而一旦处理运算逻辑在远程,例如访问外部独立数据库,或调用 WebService 完成某种操作,此时此线程就只能无谓的等待操作结束。而作为 Web 服务器处理客户端请求的线程池,其最大容纳线程的数量肯定是有限的。(虽然大多数情况下这个上限值可以修改,例如 ASP.NET 中可以通过修改 machine.config 的 processModel 标签调整最大数量,缺省25)。一旦超过此数量的请求正在并行执行,或者说正在等待后台慢速的操作,此时新来的请求就会因为处理请求线程池中无可用线程,出现虽然 CPU 负荷非常低,但仍出现 “503 服务器不可用” 类似的错误,从而事实上造成对应用的 DoS(拒绝服务)攻击。即使此上限设置很大,也会因为大量等待操作,降低其它本可以快速的页面的响应速度。

要处理这种情况,虽然可以通过继续增大请求处理线程池最大容量缓解,但总是治标不治本。更好的方法就是将请求处理和页面处理分离,避免慢速页面处理占用快速请求处理的时间。页面在接受到引擎的处理页面请求后,通过调用异步方法来试图完成实际页面处理,处理结果从单独线程池获取线程进行监控,而发送页面请求的请求处理线程将被直接释放,以便继续处理其它的页面请求。这也就是 ASP.NET 异步页面的基本思路。实际上这个思路在 ASP.NET 1.1 时就已经提出,Fritz Onion 曾于 2003 年在 MSDN 杂志发表过一篇文章详细讨论这个问题,并给出了一个简单的解决方案。

Use Threads and Build Asynchronous Handlers in Your Server-Side Web Code

文中提供的实现,很好的对此问题进行原理上的验证。但从实现角度较为繁琐,需要自行处理 IAsyncResult 接口以及自定义线程池,而且缺少对 HTTP 上下文以及超时等的处理。

好在 ASP.NET 2.0 对此问题提供了内建的支持,Jeff Prosise 在 MSDN 杂志的文章中详细的讨论了其实现思路和使用方法。

Asynchronous Pages in ASP.NET 2.0

从使用角度来说,异步页面的支持非常透明。使用者只需要在页面定义的 Page 标签中指定异步模式,例如:


<%@ Page Async="true" ... %>

然后就可以在 Page 的实现代码中,通过 Page 类型的 AddOnPreRenderCompleteAsync 或 PageAsyncTask 方法,提交异步的页面处理代码。ASP.NET 引擎会根据页面的异步模式设定,调用合适的页面处理开始和结束方法。

对大多数简单的异步处理情况,可以直接调用 AddOnPreRenderCompleteAsync 方法,提交页面请求开始和结束时的处理代码,例如上述文章中给出的一个内部处理 HTTP 页面请求的例子:

1// AsyncPage.aspx.cs

2using System;

3using System.Web;

4using System.Web.UI;

5using System.Web.UI.WebControls;

6using System.Net;

7using System.IO;

8using System.Text;

9using System.Text.RegularExpressions;

10

11public partial class AsyncPage : System.Web.UI.Page

12// AsyncPageTask.aspx.cs

2using System;

3using System.Web;

4using System.Web.UI;

5using System.Web.UI.WebControls;

6using System.Net;

7using System.IO;

8using System.Text;

9using System.Text.RegularExpressions;

10

11public partial class AsyncPageTask : System.Web.UI.Page

12class Page

2void HttpApplication.IExecutionStep.Execute()

2public class asyncpage_aspx : AsyncPage, IHttpAsyncHandler, IHttpHandler

2

14public interface IHttpAsyncHandler : IHttpHandler

15class Page

2internal class PageAsyncTaskManager

2{

3 internal void RegisterHandlersForPagePreRenderCompleteAsync()

4 {

5 _page.AddOnPreRenderCompleteAsync(new BeginEventHandler(this.BeginExecuteAsyncTasks), new EndEventHandler(this.EndExecuteAsyncTasks));

6 }

7

8 private IAsyncResult BeginExecuteAsyncTasks(object sender, EventArgs e, AsyncCallback cb, object extraData)

9 {

10 return ExecuteTasks(cb, extraData);

11 }

12

13 private void EndExecuteAsyncTasks(IAsyncResult ar)

14 {

15 _asyncResult.End();

16 }

17}

以上我们对异步页面的目的、范围、使用方式和实现原理等,有了一个大致的了解。并针对异步任务的管理做了简要的分析,基本上已经能弄清异步页面的静态运行机制如何。下一节我们将从动态执行的角度,对两级异步任务,以及相应的调度和线程使用做进一步探索。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: