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

原文地址:
http://flier-lu.blogcn.com/diary,109210941.shtml

与 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[转贴]ASP.NET 2.0 异步页面原理浅析 [1]// AsyncPage.aspx.cs
 2[转贴]ASP.NET 2.0 异步页面原理浅析 [1]using System;
 3[转贴]ASP.NET 2.0 异步页面原理浅析 [1]using System.Web;
 4[转贴]ASP.NET 2.0 异步页面原理浅析 [1]using System.Web.UI;
 5[转贴]ASP.NET 2.0 异步页面原理浅析 [1]using System.Web.UI.WebControls;
 6[转贴]ASP.NET 2.0 异步页面原理浅析 [1]using System.Net;
 7[转贴]ASP.NET 2.0 异步页面原理浅析 [1]using System.IO;
 8[转贴]ASP.NET 2.0 异步页面原理浅析 [1]using System.Text;
 9[转贴]ASP.NET 2.0 异步页面原理浅析 [1]using System.Text.RegularExpressions;
10[转贴]ASP.NET 2.0 异步页面原理浅析 [1]
11[转贴]ASP.NET 2.0 异步页面原理浅析 [1]public partial class AsyncPage : System.Web.UI.Page
12[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
13[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    private WebRequest _request;
14[转贴]ASP.NET 2.0 异步页面原理浅析 [1]
15[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    void Page_Load (object sender, EventArgs e)
16[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]    [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
17[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        AddOnPreRenderCompleteAsync (
18[转贴]ASP.NET 2.0 异步页面原理浅析 [1]            new BeginEventHandler(BeginAsyncOperation),
19[转贴]ASP.NET 2.0 异步页面原理浅析 [1]            new EndEventHandler (EndAsyncOperation)
20[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        );
21[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    }

22[转贴]ASP.NET 2.0 异步页面原理浅析 [1]
23[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    IAsyncResult BeginAsyncOperation (object sender, EventArgs e, 
24[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        AsyncCallback cb, object state)
25[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]    [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
26[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        _request = WebRequest.Create("http://msdn.microsoft.com");
27[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        return _request.BeginGetResponse (cb, state);
28[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    }

29[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    void EndAsyncOperation (IAsyncResult ar)
30[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]    [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
31[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        string text;
32[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        using (WebResponse response = _request.EndGetResponse(ar))
33[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]        [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
34[转贴]ASP.NET 2.0 异步页面原理浅析 [1]            using (StreamReader reader = 
35[转贴]ASP.NET 2.0 异步页面原理浅析 [1]                new StreamReader(response.GetResponseStream()))
36[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]            [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
37[转贴]ASP.NET 2.0 异步页面原理浅析 [1]                text = reader.ReadToEnd();
38[转贴]ASP.NET 2.0 异步页面原理浅析 [1]            }

39[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        }

40[转贴]ASP.NET 2.0 异步页面原理浅析 [1]
41[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        Regex regex = new Regex ("href\\s*=\\s*\"([^\"]*)\""
42[转贴]ASP.NET 2.0 异步页面原理浅析 [1]            RegexOptions.IgnoreCase);
43[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        MatchCollection matches = regex.Matches(text);
44[转贴]ASP.NET 2.0 异步页面原理浅析 [1]
45[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        StringBuilder builder = new StringBuilder(1024);
46[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        foreach (Match match in matches)
47[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]        [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
48[转贴]ASP.NET 2.0 异步页面原理浅析 [1]            builder.Append (match.Groups[1]);
49[转贴]ASP.NET 2.0 异步页面原理浅析 [1]            builder.Append("<br/>");
50[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        }

51[转贴]ASP.NET 2.0 异步页面原理浅析 [1]
52[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        Output.Text = builder.ToString ();
53[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    }

54[转贴]ASP.NET 2.0 异步页面原理浅析 [1]}


      AsyncPage 页面的 OnLoad 事件中提交异步处理方法;ASP.NET 引擎会在页面加载完成后,调用 BeginAsyncOperation 方法启动异步方法。这里的异步请求是打开一个远程 Web 页面,而大多数诸如数据库、WebService 调用等等,都提供了类似的异步调用版本。页面处理开始方法会返回异步调用请求的 IAsyncResult 封装,通过此接口检测处理的完成情况。而在 BeginAsyncOperation 方法返回之后,处理连接请求的线程将回到线程池,用来处理后续的连接请求。直到实际的异步处理操作完成,例如 Web 页面被取回,引擎才会从独立线程池中获取临时线程,调用 EndAsyncOperation 方法完成后续的操作。
      其实际的处理流程如下图所示:

[转贴]ASP.NET 2.0 异步页面原理浅析 [1]screen.width/2)this.style.width=screen.width/2;" border=0> 

      而 PageAsyncTask 的方式则是增强版本,除了异步页面处理开始和结束方法自身外,还可以提供在超时情况下的处理方法,以及处理时的状态对象。上述文章中给出的对应例子如下:
 1[转贴]ASP.NET 2.0 异步页面原理浅析 [1]// AsyncPageTask.aspx.cs
 2[转贴]ASP.NET 2.0 异步页面原理浅析 [1]using System;
 3[转贴]ASP.NET 2.0 异步页面原理浅析 [1]using System.Web;
 4[转贴]ASP.NET 2.0 异步页面原理浅析 [1]using System.Web.UI;
 5[转贴]ASP.NET 2.0 异步页面原理浅析 [1]using System.Web.UI.WebControls;
 6[转贴]ASP.NET 2.0 异步页面原理浅析 [1]using System.Net;
 7[转贴]ASP.NET 2.0 异步页面原理浅析 [1]using System.IO;
 8[转贴]ASP.NET 2.0 异步页面原理浅析 [1]using System.Text;
 9[转贴]ASP.NET 2.0 异步页面原理浅析 [1]using System.Text.RegularExpressions;
10[转贴]ASP.NET 2.0 异步页面原理浅析 [1]
11[转贴]ASP.NET 2.0 异步页面原理浅析 [1]public partial class AsyncPageTask : System.Web.UI.Page
12[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
13[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    private WebRequest _request;
14[转贴]ASP.NET 2.0 异步页面原理浅析 [1]
15[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    protected void Page_Load(object sender, EventArgs e)
16[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]    [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
17[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        PageAsyncTask task = new PageAsyncTask(
18[转贴]ASP.NET 2.0 异步页面原理浅析 [1]            new BeginEventHandler(BeginAsyncOperation),
19[转贴]ASP.NET 2.0 异步页面原理浅析 [1]            new EndEventHandler(EndAsyncOperation),
20[转贴]ASP.NET 2.0 异步页面原理浅析 [1]            new EndEventHandler(TimeoutAsyncOperation),
21[转贴]ASP.NET 2.0 异步页面原理浅析 [1]            null
22[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        );
23[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        RegisterAsyncTask(task);
24[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    }

25[转贴]ASP.NET 2.0 异步页面原理浅析 [1]
26[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    IAsyncResult BeginAsyncOperation(object sender, EventArgs e, 
27[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        AsyncCallback cb, object state)
28[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]    [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
29[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        _request = WebRequest.Create("http://msdn.microsoft.com");
30[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        return _request.BeginGetResponse(cb, state);
31[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    }

32[转贴]ASP.NET 2.0 异步页面原理浅析 [1]
33[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    void EndAsyncOperation(IAsyncResult ar)
34[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]    [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
35[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        string text;
36[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        using (WebResponse response = _request.EndGetResponse(ar))
37[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]        [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
38[转贴]ASP.NET 2.0 异步页面原理浅析 [1]            using (StreamReader reader = 
39[转贴]ASP.NET 2.0 异步页面原理浅析 [1]                new StreamReader(response.GetResponseStream()))
40[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]            [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
41[转贴]ASP.NET 2.0 异步页面原理浅析 [1]                text = reader.ReadToEnd();
42[转贴]ASP.NET 2.0 异步页面原理浅析 [1]            }

43[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        }

44[转贴]ASP.NET 2.0 异步页面原理浅析 [1]
45[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        Regex regex = new Regex("href\\s*=\\s*\"([^\"]*)\""
46[转贴]ASP.NET 2.0 异步页面原理浅析 [1]            RegexOptions.IgnoreCase);
47[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        MatchCollection matches = regex.Matches(text);
48[转贴]ASP.NET 2.0 异步页面原理浅析 [1]
49[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        StringBuilder builder = new StringBuilder(1024);
50[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        foreach (Match match in matches)
51[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]        [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
52[转贴]ASP.NET 2.0 异步页面原理浅析 [1]            builder.Append(match.Groups[1]);
53[转贴]ASP.NET 2.0 异步页面原理浅析 [1]            builder.Append("<br/>");
54[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        }

55[转贴]ASP.NET 2.0 异步页面原理浅析 [1]
56[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        Output.Text = builder.ToString();
57[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    }

58[转贴]ASP.NET 2.0 异步页面原理浅析 [1]
59[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    void TimeoutAsyncOperation(IAsyncResult ar)
60[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]    [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
61[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        Output.Text = "Data temporarily unavailable";
62[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    }

63[转贴]ASP.NET 2.0 异步页面原理浅析 [1]}



      为验证这一机制的实现效果,我们可以在各个方法的入口处设置断点。因为 VS2005 的 IDE 屏蔽了底层 CLR 实现信息,我们需要在 Debug\Windows\Immediate Window 窗口中,输入 .load sos 命令加载 CLR 调试支持。具体的 sos 命令可以输入 !help 查询帮助,或参考我以前《用WinDbg探索CLR世界》的系列文章,这里不再罗嗦。

      在 AsyncPage 类型的 Page_Load、BeginAsyncOperation 和 EndAsyncOperation 方法中,分别输入 !ClrStack 命令可以获取当前线程的调用堆栈:

!clrstack
OS Thread Id: 0xb00 (2816)
ESP       EIP     
0361d9d4 05a9c817 AsyncPage.Page_Load(System.Object, System.EventArgs)
...
0361df34 050dd403 System.Web.HttpRuntime.ProcessRequest(System.Web.HttpWorkerRequest)
...

!clrstack
OS Thread Id: 0xb00 (2816)
ESP       EIP     
0361dc04 05a9d819 AsyncPage.BeginAsyncOperation(System.Object, System.EventArgs, System.AsyncCallback, System.Object)
...
0361df34 050dd403 System.Web.HttpRuntime.ProcessRequest(System.Web.HttpWorkerRequest)
...

!clrstack
OS Thread Id: 0xd30 (3376)
ESP       EIP     
04c5eee0 05fef30f AsyncPage.EndAsyncOperation(System.IAsyncResult)
...
04c5f60c 05fe976c System.Net.Connection.ReadComplete(Int32, System.Net.WebExceptionStatus)
...


      可以看到 Page_Load 和 BeginAsyncOperation 方法都是在 ID 为 2816 的线程中被调用,其调用源也都是处理 HTTP 请求的 HttpRuntime.ProcessRequest 方法;而 EndAsyncOperation 则是在另外一个 ID 为 3376 的线程中调用,调用源也是完成网络读操作的 Connection.ReadComplete 方法。

      而从实现角度来看,AddOnPreRenderCompleteAsync 方法将异步页面处理的启动和停止方法,放到一个 Page.PageAsyncInfo 对象中。此对象维护了与页面相关的各种上下文信息,以及开始、停止和状态的数组。而 RegisterAsyncTask 方法也是类似,将 PageAsyncTask 实例放到 PageAsyncTaskManager 类型的管理器中。

 
 1[转贴]ASP.NET 2.0 异步页面原理浅析 [1]class Page
 2[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
 3[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    private Page.PageAsyncInfo _asyncInfo;
 4[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    private PageAsyncTaskManager _asyncTaskManager;
 5[转贴]ASP.NET 2.0 异步页面原理浅析 [1]
 6[转贴]ASP.NET 2.0 异步页面原理浅析 [1]  public void AddOnPreRenderCompleteAsync(BeginEventHandler beginHandler, EndEventHandler endHandler, object state)
 7[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]  [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
 8[转贴]ASP.NET 2.0 异步页面原理浅析 [1]      // 处理参数和状态异常情况
 9[转贴]ASP.NET 2.0 异步页面原理浅析 [1]      
10[转贴]ASP.NET 2.0 异步页面原理浅析 [1]      // 延迟构造异步页面信息
11[转贴]ASP.NET 2.0 异步页面原理浅析 [1]      if (_asyncInfo == null)      
12[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        _asyncInfo = new Page.PageAsyncInfo(this);  
13[转贴]ASP.NET 2.0 异步页面原理浅析 [1]      
14[转贴]ASP.NET 2.0 异步页面原理浅析 [1]      _asyncInfo.AddHandler(beginHandler, endHandler, state);
15[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    }

16[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    
17[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    public void RegisterAsyncTask(PageAsyncTask task)
18[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]    [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
19[转贴]ASP.NET 2.0 异步页面原理浅析 [1]      // 处理参数和状态异常情况
20[转贴]ASP.NET 2.0 异步页面原理浅析 [1]      
21[转贴]ASP.NET 2.0 异步页面原理浅析 [1]      // 延迟构造异步任务管理器
22[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    if (this._asyncTaskManager == null)
23[转贴]ASP.NET 2.0 异步页面原理浅析 [1]      _asyncTaskManager = new PageAsyncTaskManager(this);
24[转贴]ASP.NET 2.0 异步页面原理浅析 [1]     
25[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    _asyncTaskManager.AddTask(task);
26[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    }

27[转贴]ASP.NET 2.0 异步页面原理浅析 [1]}


      HttpApplication 在处理页面请求时,通过其 pipeline 的 CallHandlerExecutionStep 步骤,调用页面的 BeginProcessRequest 方法,其伪代码如下:
 1[转贴]ASP.NET 2.0 异步页面原理浅析 [1]void HttpApplication.IExecutionStep.Execute()
 2[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
 3[转贴]ASP.NET 2.0 异步页面原理浅析 [1]  // 从上下文中获取获取当前页面的处理器
 4[转贴]ASP.NET 2.0 异步页面原理浅析 [1]  HttpContext context = _application.Context;
 5[转贴]ASP.NET 2.0 异步页面原理浅析 [1]  IHttpHandler handler = context.Handler;
 6[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    
 7[转贴]ASP.NET 2.0 异步页面原理浅析 [1]  if (handler == null)
 8[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]  [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
 9[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    _sync = true;
10[转贴]ASP.NET 2.0 异步页面原理浅析 [1]  }

11[转贴]ASP.NET 2.0 异步页面原理浅析 [1]  else if (handler is IHttpAsyncHandler)
12[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]  [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
13[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    // 如果是异步处理器,则调用异步处理开始方法
14[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler) handler1;
15[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    
16[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    _sync = false;
17[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    _handler = asyncHandler;
18[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    
19[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    IAsyncResult result = asyncHandler.BeginProcessRequest(context, _completionCallback, null);
20[转贴]ASP.NET 2.0 异步页面原理浅析 [1]            
21[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        // 如果的确是异步操作,就直接返回            
22[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        if (!result.CompletedSynchronously)
23[转贴]ASP.NET 2.0 异步页面原理浅析 [1]            return;
24[转贴]ASP.NET 2.0 异步页面原理浅析 [1]      
25[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    // 否则恢复同步的页面处理流程
26[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    _sync = true;
27[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    _handler = null;
28[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    asyncHandler.EndProcessRequest(result);
29[转贴]ASP.NET 2.0 异步页面原理浅析 [1]  }

30[转贴]ASP.NET 2.0 异步页面原理浅析 [1]  else
31[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]  [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
32[转贴]ASP.NET 2.0 异步页面原理浅析 [1]      // 采用同步模式处理页面
33[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    _sync = true;
34[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    
35[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    _application.SyncContext.SetSyncCaller();
36[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    try
37[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]    [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
38[转贴]ASP.NET 2.0 异步页面原理浅析 [1]      handler.ProcessRequest(context);
39[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    }

40[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    finally
41[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]    [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
42[转贴]ASP.NET 2.0 异步页面原理浅析 [1]      _application.SyncContext.ResetSyncCaller();
43[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    }

44[转贴]ASP.NET 2.0 异步页面原理浅析 [1]  }

45[转贴]ASP.NET 2.0 异步页面原理浅析 [1]}


      而 ASP.NET 页面一旦通过 Page 标记定义为异步模式,其编译生成的 Page 子类就会实现 IHttpAsyncHandler 接口。
      例如对上述例子,我们可以通过 !ClrStack 命令看到页面被编译为名称为 ASP.asyncpage_aspx 的类型。

!clrstack
OS Thread Id: 0xb00 (2816)
ESP       EIP     
0361d9d4 05a9c817 AsyncPage.Page_Load(System.Object, System.EventArgs)
...
0361dd5c 0545b90e System.Web.UI.Page.AsyncPageBeginProcessRequest(System.Web.HttpContext, System.AsyncCallback, System.Object)
0361dd98 0545b813 ASP.asyncpage_aspx.BeginProcessRequest(System.Web.HttpContext, System.AsyncCallback, System.Object)
0361ddec 0545b6b6 System.Web.HttpApplication+CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
0361de28 05400c18 System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef)
...


      进一步使用 !DumpDomain 命令可以找到其页面编译的临时文件,如

!DumpDomain 

...

Assembly: 0023ade0 [D:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\asyncpage\ba0a3865\3cd7d92\App_Web_n97gem4v.dll]
ClassLoader: 0023abc0
SecurityDescriptor: 00229ac0
  Module Name
05a502cc D:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files\asyncpage\ba0a3865\3cd7d92\App_Web_n97gem4v.dll

...


      使用 IL 反汇编根据打开此文件,可以看到 AsyncPage.aspx 被编译为 asyncpage_aspx 类型,如下所示:
 1[转贴]ASP.NET 2.0 异步页面原理浅析 [1]public class asyncpage_aspx : AsyncPage, IHttpAsyncHandler, IHttpHandler
 2[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
 3[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    public virtual IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object data)
 4[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]    [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
 5[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    return base.AsyncPageBeginProcessRequest(context, cb, data);
 6[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    }

 7[转贴]ASP.NET 2.0 异步页面原理浅析 [1] 
 8[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    public virtual void EndProcessRequest(IAsyncResult ar)
 9[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]    [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{    
10[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    base.AsyncPageEndProcessRequest(ar);
11[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    }

12[转贴]ASP.NET 2.0 异步页面原理浅析 [1]}

13[转贴]ASP.NET 2.0 异步页面原理浅析 [1]
14[转贴]ASP.NET 2.0 异步页面原理浅析 [1]public interface IHttpAsyncHandler : IHttpHandler
15[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
16[转贴]ASP.NET 2.0 异步页面原理浅析 [1]  // Methods
17[转贴]ASP.NET 2.0 异步页面原理浅析 [1]  IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData);
18[转贴]ASP.NET 2.0 异步页面原理浅析 [1]  void EndProcessRequest(IAsyncResult result);
19[转贴]ASP.NET 2.0 异步页面原理浅析 [1]}


      其中 AsyncPage 类型是后台实现代码编译生成的类型,asyncpage_aspx 则是 .aspx 页面编译生成。

      而在 Page.AsyncPageBeginProcessRequest 方法中,将首先处理上下文环境初始化,初始化异步执行信息,以及相应回调函数的执行;然后会调用 PageAsyncTaskManager.RegisterHandlersForPagePreRenderCompleteAsync 将异步任务管理器中所有的异步任务,封装后注册到 Page.PageAsyncInfo 对象中维护的异步调用信息中;最后调用      其 CallHandlers 方法完成对异步处理开始方法的调用。完整的伪代码如下:
 1[转贴]ASP.NET 2.0 异步页面原理浅析 [1]class Page
 2[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
 3[转贴]ASP.NET 2.0 异步页面原理浅析 [1]  protected IAsyncResult AsyncPageBeginProcessRequest(HttpContext context, AsyncCallback callback, object extraData)
 4[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]    [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
 5[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        // 处理上下文环境初始化
 6[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        
 7[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        // 初始化异步执行信息
 8[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        _asyncInfo.AsyncResult = new HttpAsyncResult(callback, extraData);
 9[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    _asyncInfo.CallerIsBlocking = callback == null;
10[转贴]ASP.NET 2.0 异步页面原理浅析 [1]
11[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        // 执行相应回调函数
12[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        
13[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        // 注册异步任务
14[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        if ((_asyncTaskManager != null&& !_asyncInfo.CallerIsBlocking)    
15[转贴]ASP.NET 2.0 异步页面原理浅析 [1]      _asyncTaskManager.RegisterHandlersForPagePreRenderCompleteAsync();
16[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    
17[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    // 调用所有的异步处理开始方法
18[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        _asyncInfo.CallHandlers(true);
19[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    
20[转贴]ASP.NET 2.0 异步页面原理浅析 [1]        return _asyncInfo.AsyncResult;    
21[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    }

22[转贴]ASP.NET 2.0 异步页面原理浅析 [1]}


      而在 PageAsyncTaskManager 中被管理的异步任务,会作为一个异步执行信息注册到 PageAsyncInfo 中去。并在其被调用时,实际调用 PageAsyncTaskManager 类型的 ExecuteTasks 方法,实现较为复杂的异步调用逻辑。
 1[转贴]ASP.NET 2.0 异步页面原理浅析 [1]internal class PageAsyncTaskManager
 2[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
 3[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    internal void RegisterHandlersForPagePreRenderCompleteAsync()
 4[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]    [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
 5[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    _page.AddOnPreRenderCompleteAsync(new BeginEventHandler(this.BeginExecuteAsyncTasks), new EndEventHandler(this.EndExecuteAsyncTasks));
 6[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    }

 7[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    
 8[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    private IAsyncResult BeginExecuteAsyncTasks(object sender, EventArgs e, AsyncCallback cb, object extraData)
 9[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]    [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
10[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    return ExecuteTasks(cb, extraData);
11[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    }

12[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    
13[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    private void EndExecuteAsyncTasks(IAsyncResult ar)
14[转贴]ASP.NET 2.0 异步页面原理浅析 [1][转贴]ASP.NET 2.0 异步页面原理浅析 [1]    [转贴]ASP.NET 2.0 异步页面原理浅析 [1]{
15[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    _asyncResult.End();
16[转贴]ASP.NET 2.0 异步页面原理浅析 [1]    }

17[转贴]ASP.NET 2.0 异步页面原理浅析 [1]}



      以上我们对异步页面的目的、范围、使用方式和实现原理等,有了一个大致的了解。并针对异步任务的管理做了简要的分析,基本上已经能弄清异步页面的静态运行机制如何。下一节我们将从动态执行的角度,对两级异步任务,以及相应的调度和线程使用做进一步探索。

to be continue...
上一篇:功能强大的系统工具Sysdig命令实例介绍


下一篇:数据库基础题(求每个班的平均分)