原文链接:http://dotnetslackers.com/articles/iis/ASPNETInternalsIISAndTheProcessModel2.aspx
作者:Simone Busoli
日期:6.13.2007

   ASP.NET的内部机制有时就象是舞台背后的魔术,给开发者提供一个高度抽象的层面,把注意力集中在应用程序的逻辑上,而不需要去处理Http协议的底层机制。ASP.NET建立在.NET框架上,从而大多是托管代码。现在,Web请求的入口被本地代码编写的Web服务器,所以往往需要一套通信机制。本篇文章准备描述托管代码与非托管代码之间的桥梁以及两者如何相互协作搭建起处理ASP.NET请求的环境。
   请参见:ASP.NET Internals – IIS and the Process Model

简介
   在这一系列的先前的文章中我们已经介绍了第一步,一个Web请求被Web服务器如何接收以及通过什么样的途径最终被标识为ASP.NET资源的请求。你已经看到了不同版本的IIS如何处理到来的ASP.NET请求,直到它们最终被分派到一个叫aspnet_isapi.dll的非托管Win32组件,它是Web服务器与托管ASP.NET之间的桥梁。
   在这篇文章中我准备从上篇文章的基础上继续深入的讨论,并且开始研究托管部分的进程环境,也就是先前提到过的称之为黑箱的ASP.NET Http运行时环境。
   注:ASP.NET运行时环境也常被称为ASP.NET管道,Http运行时管道,或两种说法的混合。

离开aspnet_isapi.dll进入托管世界
   先前的文章中我已经解释了IIS5.0和6.0是怎样管理Asp.net请求的。不管它是怎样处理Worker Process的生成,管理和回收,请求的所有信息最后都交由aspnet_isapi.dll处理。
   它是托管与非托管世界之间的桥梁,也是ASP.NET体系架构中正式文档最少的部分。
   注:在我写这篇文章的时候IIS7.0刚刚在Longhorn Server Beta3和Go Live通行证一起发布。很多东西会在IIS7.0中改变,虽然以前的文章都是针对某个IIS的版本,但是以后的文章会着重介绍IIS7.0的改变及增强。
   我们讨论的主题因为缺少现成的文档资料,所以我下面解释的也不一定正确,尤其是非托管体系架构那块。所幸的是我们对这个非正式主题的讨论是基于一些工具的,这些工具帮助我们理解框架的工作原理:
   Lutz Roeder's .NET Reflector,静态分析及反编译.NET托管类的代码
   Microsoft CLR Profiler,通过函数调用,内存分配,类实例及其他很多特征分析框架的动态行为
   JetBrains dotTrace Profiler,一个商业的.NET应用程序剖析器,也可以得到限时的版本
   Red Gate ANTS Profiler,另一个商业的.NET应用程序剖析器,也可以得到限时的版本
   下图为译者添加:

   回到托管与非托管世界间的桥梁,一旦CLR通过IIS6.0进程模块的ISAPI扩展或IIS5.0进程模块的Worker Process装载,ASP.NET ISAPI组件通过一系列包含于命名空间System.Web.Hosting的托管类调用一系列非托管COM接口,AppManagerAppDomainFactory 类和ISAPIRuntime类暴露了一些COM接口的方法。
   注:CLR,公共语言运行时,代表所有.NET应用程序的执行环境。实际上它对运行时托管应用程序提供环境及服务。它被Win32进程宿主,而ASP.NET是框架CLR提供的其中一个宿主。特别地,在ASP.NET中ASP.NET Worker Process(IIS5.0对应aspnet_wp.exe,IIS6.0对应w3wp.exe)是宿主在CLR中的一个进程。
   在深入研究这些类之间的交互之前,让我们先表面地看看请求是如何被处理的。我已经在前面介绍了两个类,因为处理请求的入口可粗略地分成两方面:
   1、建立一个AppDomain,如果还没存在,将AppDomain委派给相应的请求定位的应用程序,它由AppManagerAppDomain类完成。
   2、提交与处理请求,由ISAPIRuntime类完成。 
 尽管两方面都很重要,第一类由没有开发者介入期望发生的交互组成,它关心应用程序的hosting 环境;第二类主要是基础组织的结构部分,在下面的后续中会对它进行详细的研究。
 从另一面看,第一类由一系列操作组成,这些操作只在应用程序生命周期中执行一次,那就是初始化的时候;而第二类是由一些发生在每个请求定位到特定应用程序的交互组成。


创建AppDomain
   前面我们已经知道,一个ASP.NET 应用程序被保留且封装成一个叫应用程序域的实体,aka AppDomain,ASP.NET体系架构中的AppDomain 类来实现。当特定应用程序的请求到达时,一个AppDomain建立,如果还没建立。这通常发生在进来的请求是第一个定位指定应用程序的情况,或有些原因导致相应的AppDomain被关闭,具体原因下面会做出分析。注意一个单一的应用程序只能有一个AppDomain,和IIS应用程序成一一映射的关系,可能是基于物理或虚拟目录建立。那么这个类的实例是怎样创建的呢?
   图1:创建AppDomain实例堆栈图,JetBrains dotTrace Profiler

   利用Reflector我们可以得知AppDomain类有一抛出NotSupportedException异常的私有构造器。很明显不能通过它来创建。实际上,实例化一个AppDomain的入口处是AppManagerAppDomainFactory.Create方法。为了找出它,有必要利用profiler 工具去查看当实例化对象时调用堆栈的踪迹。图1是由JetBrains dotTrace Profiler截的图,显示了宿主在IIS上的web应用程序实例化AppDomain的堆栈踪迹。很多类参与在这过程中,但是有些类开发者可能永远也不会去接触它。
   注意到在CLR初始化过程中AppManagerAppDomainFactory类只被实例化一次。就如它的名字,它也可以创建其他对象。
   图2显示的是和前面一样的调用堆栈图,这次是由Microsoft CLR Profiler工具捕获的。在实例化AppDomain的过程中它提供了更详细的信息。事实上,高亮显示的行显示了两个AppDomain 类实例被主线程创建。
   图2:创建AppDomain实例过程中堆栈调用图,Microsoft CLR Profiler

   那么额外的实例是怎样创建的,为什么会创建?不幸地这不是一个简单的问题。上图中显示协调实际应用程序执行的AppDomain实例化时堆栈调用的步骤,所以这个额外的实例看起来象是一个辅助性的对象,用来实现某些难以理解的目的。我猜测,它可能被用在所有宿主在不属于指定应用程序的对象上,也就是说被宿主在隔离的AppDomain上。至于AppManagerAppDomainFactory类,这个辅助性的AppDomain 在CLR初始化期间只被实例化一次,在整个步骤非常早的时候,见图3中所显示的。
   图3:额外AppDomain实例方法的次序及分配

   图3显示了Red Gate ANTS Profiler捕获的截图。它显示了在CLR初始化期间的非常早期额外的AppDomain实例被创建。它明显比AppManagerAppDomainFactory类创建要早,或许它是它的容器。实际上,单独的
AppManagerAppDomainFactory实例的ID,在剖析工具中会转变为52。

   另外一种查看初始化过程中两个AppDomains的方法,Microsoft CLR Profiler分配视图。它创建一图形流程来描述在初始化过程中调用的类,见图4。
   图4:实例化两个AppDomains,Microsoft CLR Profiler分配图

   图中描述了一个概览,切掉了<root>元素CLR及Thread类之间的所有类。尽管这样,它还是清楚的显示了当创建两个AppDomain实例时的顺序。
   另一个可从图中获取有关AppDomain实例的信息是每个实例在内存中占100bytes。这倒不是很有关系,但是有人可能会认为AppDomain实例是一个很大的类,因为它要宿主在应用程序中。实际上,它提供很多服务但存储很少的数据。
   到现在为止,辅助性的AppDomain实例和AppManagerAppDomainFactory实例一样已经被创建,图1和图2中显示了它的Create方法是调用堆栈中的入口。
   这个方法通过COM被暴露及调用,因此是由非托管代码实现的。AppManagerAppDomainFactory 类实现了接口IAppManagerAppDomainFactory,图5中描述了它的构造。
   图5:IAppManagerAppDomainFactory接口的构造

    这个接口由ComImportInterfaceType属性修饰,它们把类型绑定到非托管接口类型中。当这个方法被调用时它产生图1和图2所示的调用堆栈,最后触发创建AppDomain类实例,这个实例通常宿主在请求定位的web应用程序。
   AppDomain一旦创建,运作所有剩下的工作,比目前已经做的更加重要,那就是处理请求。ASP.NET体系架构中最有趣的部分可能就是它了,但在接触这有趣的部分之前,我想先介绍一些核心的主题。

总结
   在这篇文章中我已经介绍了一些有关ASP.NET基础架构的底层的主题,研究由ASP.NET ISAPI 扩展描述的非托管与托管世界间的接口,表现为AppDomain宿主在web应用程序中。我介绍了AppDomain如何实例化,过程中参与了哪些类。下一篇文章我将讨论托管代码,通过我个人认为在ASP.NET结构中非常有吸引力的HTTP管道来描述,它正是区别于其他web开发框架的主要技术。

参考
  • Simone Busoli - ASP.NET Internals – IIS and the Process Model
  • Rick Strahl - A low-level Look at the ASP.NET Architecture
  • Dino Esposito – Programming Microsoft ASP.NET 2.0 Core Reference
  • Dino Esposito – Programming Microsoft ASP.NET 2.0 Applications Advanced Topics