知识大全 ASP.NET 2.0异步页面原理浅析

Posted

篇首语:实践是知识的母亲,知识是生活的明灯。本文由小常识网(cha138.com)小编为大家整理,主要介绍了知识大全 ASP.NET 2.0异步页面原理浅析相关的知识,希望对你有一定的参考价值。

ASP.NET 2.0异步页面原理浅析  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  Asynchronous Pages in ASP NET

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

  <%@ Page Async= true %>

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

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

   // AsyncPage aspx cs

   using System;

   using System Web;

   using System Web UI;

   using System Web UI WebControls;

   using System Net;

   using System IO;

   using System Text;

   using System Text RegularExpressions;

  

   public partial class AsyncPage : System Web UI Page

  

       private WebRequest _request;

  

       void Page_Load (object sender EventArgs e)

      

           AddOnPreRenderCompleteAsync (

               new BeginEventHandler(BeginAsyncOperation)

               new EndEventHandler (EndAsyncOperation)

           );

      

  

       IAsyncResult BeginAsyncOperation (object sender EventArgs e

           AsyncCallback cb object state)

      

           _request = WebRequest Create( );

           return _request BeginGetResponse (cb state);

      

       void EndAsyncOperation (IAsyncResult ar)

      

           string text;

           using (WebResponse response = _request EndGetResponse(ar))

          

               using (StreamReader reader =

                   new StreamReader(response GetResponseStream()))

              

                   text = reader ReadToEnd();

              

          

  

           Regex regex = new Regex ( href\\\\s*=\\\\s*\\ ([^\\ ]*)\\

               RegexOptions IgnoreCase);

           MatchCollection matches = regex Matches(text);

  

           StringBuilder builder = new StringBuilder( );

           foreach (Match match in matches)

          

               builder Append (match Groups[ ]);

               builder Append( <br/> );

          

  

           Output Text = builder ToString ();

      

  

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

  而 PageAsyncTask 的方式则是增强版本 除了异步页面处理开始和结束方法自 身外 还可以提供在超时情况下的处理方法 以及处理时的状态对象 上述文章中给出的对应例子如下

   // AsyncPageTask aspx cs

   using System;

   using System Web;

   using System Web UI;

   using System Web UI WebControls;

   using System Net;

   using System IO;

   using System Text;

   using System Text RegularExpressions;

  

   public partial class AsyncPageTask : System Web UI Page

  

       private WebRequest _request;

  

       protected void Page_Load(object sender EventArgs e)

      

           PageAsyncTask task = new PageAsyncTask(

               new BeginEventHandler(BeginAsyncOperation)

               new EndEventHandler(EndAsyncOperation)

               new EndEventHandler(TimeoutAsyncOperation)

               null

           );

           RegisterAsyncTask(task);

      

  

       IAsyncResult BeginAsyncOperation(object sender EventArgs e

           AsyncCallback cb object state)

      

           _request = WebRequest Create( );

           return _request BeginGetResponse(cb state);

      

  

       void EndAsyncOperation(IAsyncResult ar)

      

           string text;

           using (WebResponse response = _request EndGetResponse(ar))

          

               using (StreamReader reader =

                   new StreamReader(response GetResponseStream()))

              

                   text = reader ReadToEnd();

              

          

  

           Regex regex = new Regex( href\\\\s*=\\\\s*\\ ([^\\ ]*)\\

               RegexOptions IgnoreCase);

           MatchCollection matches = regex Matches(text);

  

           StringBuilder builder = new StringBuilder( );

           foreach (Match match in matches)

          

               builder Append(match Groups[ ]);

               builder Append( <br/> );

          

  

           Output Text = builder ToString();

      

  

       void TimeoutAsyncOperation(IAsyncResult ar)

      

           Output Text = Data temporarily unavailable ;

      

  

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

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

  !clrstack

  OS Thread Id: xb ( )

  ESP       EIP

   d d a c AsyncPage Page_Load(System Object System EventArgs)

  

   df dd System Web HttpRuntime ProcessRequest(System Web HttpWorkerRequest)

  

  !clrstack

  OS Thread Id: xb ( )

  ESP       EIP

   dc a d AsyncPage BeginAsyncOperation(System Object System EventArgs System AsyncCallback System Object)

  

   df dd System Web HttpRuntime ProcessRequest(System Web HttpWorkerRequest)

  

  !clrstack

  OS Thread Id: xd ( )

  ESP       EIP

   c eee fef f AsyncPage EndAsyncOperation(System IAsyncResult)

  

   c f c fe c System Net Connection ReadComplete(Int System Net WebExceptionStatus)

  

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

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

   class Page

  

       private Page PageAsyncInfo _asyncInfo;

       private PageAsyncTaskManager _asyncTaskManager;

  

     public void AddOnPreRenderCompleteAsync(BeginEventHandler beginHandler EndEventHandler endHandler object state)

    

         // 处理参数和状态异常情况

  

         // 延迟构造异步页面信息

         if (_asyncInfo == null)

           _asyncInfo = new Page PageAsyncInfo(this);

  

         _asyncInfo AddHandler(beginHandler endHandler state);

      

  

       public void RegisterAsyncTask(PageAsyncTask task)

      

         // 处理参数和状态异常情况

  

         // 延迟构造异步任务管理器

       if (this _asyncTaskManager == null)

         _asyncTaskManager = new PageAsyncTaskManager(this);

  

       _asyncTaskManager AddTask(task);

      

  

  HttpApplication 在处理页面请求 时 通过其 pipeline 的 CallHandlerExecutionStep 步骤 调用页面的 BeginProcessRequest 方 法 其伪代码如下

   void HttpApplication IExecutionStep Execute()

  

     // 从上下文中获取获取当前页面的处理器

     HttpContext context = _application Context;

     IHttpHandler handler = context Handler;

  

     if (handler == null)

    

       _sync = true;

    

     else if (handler is IHttpAsyncHandler)

    

       // 如果是异步处理器 则调用异步处理开始方法

           IHttpAsyncHandler asyncHandler = (IHttpAsyncHandler) handler ;

  

       _sync = false;

       _handler = asyncHandler;

  

       IAsyncResult result = asyncHandler BeginProcessRequest(context _pletionCallback null);

  

           // 如果的确是异步操作 就直接返回

           if (!result CompletedSynchronously)

               return;

  

       // 否则恢复同步的页面处理流程

       _sync = true;

       _handler = null;

       asyncHandler EndProcessRequest(result);

    

     else

    

         // 采用同步模式处理页面

       _sync = true;

  

       _application SyncContext SetSyncCaller();

       try

      

         handler ProcessRequest(context);

      

       finally

      

         _application SyncContext ResetSyncCaller();

      

    

  

  而 ASP NET 页面一旦通过 Page 标记 定义为异步模式 其编译生成的 Page 子类就会实现 IHttpAsyncHandler 接口

  例如对上述例子 我们可以通 过 !ClrStack 命令看到页面被编译为名称为 ASP asyncpage_aspx 的类型

  !clrstack

  OS Thread Id: xb ( )

  ESP       EIP

   d d a c AsyncPage Page_Load(System Object System EventArgs)

  

   dd c b e System Web UI Page AsyncPageBeginProcessRequest(System Web HttpContext System AsyncCallback System Object)

   dd b ASP asyncpage_aspx BeginProcessRequest(System Web HttpContext System AsyncCallback System Object)

   ddec b b System Web HttpApplication+CallHandlerExecutionStep System Web HttpApplication IExecutionStep Execute()

   de c System Web HttpApplication ExecuteStep(IExecutionStep Boolean ByRef)

  

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

  !DumpDomain

  

  Assembly: ade [D:\\WINDOWS\\Microsoft NET\\Framework\\v \\Temporary ASP NET Files\\asyncpage\\ba a \\ cd d \\App_Web_n gem v dll]

  ClassLoader: abc

  SecurityDescriptor: ac

  Module Name

   a cc D:\\WINDOWS\\Microsoft NET\\Framework\\v \\Temporary ASP NET Files\\asyncpage\\ba a \\ cd d \\App_Web_n gem v dll

  

  使 用 IL 反汇编根据打开此文件 可以看到 AsyncPage aspx 被编译为 asyncpage_aspx 类型 如下所示

   public class asyncpage_aspx : AsyncPage IHttpAsyncHandler IHttpHandler

  

       public virtual IAsyncResult BeginProcessRequest(HttpContext context AsyncCallback cb object data)

      

       return base AsyncPageBeginProcessRequest(context cb data);

      

  

       public virtual void EndProcessRequest(IAsyncResult ar)

      

       base AsyncPageEndProcessRequest(ar);

      

  

  

   public interface IHttpAsyncHandler : IHttpHandler

  

     // Methods

     IAsyncResult BeginProcessRequest(HttpContext context AsyncCallback cb object extraData);

     void EndProcessRequest(IAsyncResult result);

  

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

  而 在 Page AsyncPageBeginProcessRequest 方法中 将首先处理上下文环境初始化 初始化异步执行信息 以及相应回调函数 的执行 然后会调 用 PageAsyncTaskManager RegisterHandlersForPagePreRenderCompleteAsync 将异步 任务管理器中所有的异步任务 封装后注册到 Page PageAsyncInfo 对象中维护的异步调用信息中 最后调 用      其 CallHandlers 方法完成对异步处理开始方法的调用 完整的伪代码如下

   class Page

  

     protected IAsyncResult AsyncPageBeginProcessRequest(HttpContext context AsyncCallback callback object extraData)

      

           // 处理上下文环境初始化

  

           // 初始化异步执行信息

           _asyncInfo AsyncResult = new HttpAsyncResult(callback extraData);

       _asyncInfo CallerIsBlocking = callback == null;

  

           // 执行相应回调函数

  

           // 注册异步任务

           if ((_asyncTaskManager != null) && !_asyncInfo CallerIsBlocking)

         _asyncTaskManager RegisterHandlersForPagePreRenderCompleteAsync();

  

       // 调用所有的异步处理开始方法

           _asyncInfo CallHandlers(true);

  

           return _asyncInfo AsyncResult;

      

  

  而 在 PageAsyncTaskManager 中被管理的异步任务 会作为一个异步执行信息注册到 PageAsyncInfo 中去 并在其被调用 时 实际调用 PageAsyncTaskManager 类型的 ExecuteTasks 方法 实现较为复杂的异步调用逻辑

   internal class PageAsyncTaskManager

  

       internal void RegisterHandlersForPagePreRenderCompleteAsync()

      

       _page AddOnPreRenderCompleteAsync(new BeginEventHandler(this BeginExecuteAsyncTasks) new EndEventHandler(this EndExecuteAsyncTasks));

      

  

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

      

       return ExecuteTasks(cb extraData);

      

  

       private void EndExecuteAsyncTasks(IAsyncResult ar)

      

       _asyncResult End();

      

  

cha138/Article/program/net/201311/13118

相关参考

知识大全 浅析ASP.NET 2.0 Client Callback

浅析ASP.NET2.0ClientCallback  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

知识大全 ASP.NET 2.0页面框架简要慨述

ASP.NET2.0页面框架简要慨述  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  ASPNET

知识大全 在ASP.NET 2.0中使用页面导航控件

在ASP.NET2.0中使用页面导航控件  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  几乎每个

知识大全 Asp.NET 2.0中无刷新页面的开发

Asp.NET2.0中无刷新页面的开发  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  在已经发布

知识大全 asp.net 2.0中使用sitemapDATAsource做页面导航

asp.net2.0中使用sitemapDATAsource做页面导航  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶

知识大全 ASP.NET生成静态页面和分页主要的原理

ASP.NET生成静态页面和分页主要的原理  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!  静态模

知识大全 如何由ASP.NET 1.1移植到ASP.NET 2.0

如何由ASP.NET1.1移植到ASP.NET2.0  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!

知识大全 Asp.net异步如何提高服务器吞吐量

Asp.net异步如何提高服务器吞吐量  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!Codepub

知识大全 ASP.NET入门教程 7.2 ASP.NET 2.0的数据控件

ASP.NET入门教程7.2ASP.NET2.0的数据控件  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一

知识大全 为ASP.NET MVC扩展异步Action功能(上)

为ASP.NETMVC扩展异步Action功能(上)  以下文字资料是由(全榜网网www.cha138.com)小编为大家搜集整理后发布的内容,让我们赶快一起来看一下吧!