原文链接:http://dotnetslackers.com/articles/iis/ASPNETInternalsIISAndTheProcessModel.aspx
发布日期:5-3-2007
作者:Simone Busoli
   ASP.NET是一个构建web应用程序和组件的漂亮框架,但由于它的扩展性使得绝大多数人无法了解它的每个细节。尽管通过网络或图书可以找到很多可用的资料,但我也发现关于底层架构的资料非常缺少,比如理解它的工作原理及高层次的设计取舍的一些必要的基本信息。在这一系列的文章中,我将会描述web请求的生命周期,从最初的被web服务器接收,经过处理路由至ASP.NET管道,最后到管道的终端并产生响应。

介绍
   MS的Active Server Pages,也叫ASP,自从1996首次发布以来,给web开发人员提供了一个丰富、复杂的框架去创建web应用程序。过去的几年基础框架得到了迅速发展,成为大家所熟知的ASP.NET,不再象它的前身。ASP.NET是创建web应用程序的框架,也就是说应用程序运行在web之上,client-server模式表现为浏览器向服务器发送各种类型的请求。在诸如CGI, PHP, JSP and ASP等服务器端动态资源响应技术到来之前,所有的服务器不得不接收来自client的静态资源的请求然后把它们发送回请求者。随着动态技术的发展,web服务器需要承担更多的责任,因此开发人员必须找出在服务器端产生动态资源并将结果返回client的途径,这是一项不同于以往的工作。
   从一个菜鸟的观点看,client和server之间的交互是乎很简单。就是出现了使用HTTP (Hyper Text Transfer Protocol)协议的网络通讯,HTTP协议是应用层协议,依靠TCP和IP在异构网络World Wide Web上的两个结点之间传输数据。
   每一种服务器端动态技术必须清楚特定web服务器的实现,ASP.NET是和MS的Internet Information Serve即IIS紧密结合在一起的。
   不同的服务器选择不同的途径产生动态资源,接下来我们要检验的是IIS是如何处理请求的。

IIS和ISAPI扩展
   前面提及,静态资源不需要让服务器产生,一旦这种请求到来时,服务器仅仅要做的是从文件系统中获取它的内容,然后通过HTTP协议以字节流的形式发回至client。静态资源可以是图像,脚本文件,CSS样式表或html页面。很明显服务器必须能判别静态和动态资源,因为后者需要经过处理而不能直接返回给client。这就是ISAPI扩展出现的原因,ISAPI代表因特网服务应用程序编程接口。ISAPI扩展是Win32 .dll实现的模板,IIS依赖它处理特定的资源。ISAPI扩展和文件之间的映射通过IIS插件配置,并被保存在IIS元数据中,每种文件扩展都能与特定的ISAPI扩展关联,也就是说,当一个此类文件的请求到来时,IIS用相应的ISAPI扩展处理它,以确保能处理。
   图1:IIS5.0中配置ISAPI扩展

   明显地ISAPI扩展必须实现一个公共接口,通过IIS调用,提供请求的必要数据,产生响应。
   如图1所示,.asp扩展映射到.dll ISAPI 扩展;对于ASP页面,这个组件负责处理所有的请求及产生响应,也就是说,它收集请求的信息,通过Request, Response 及其它ASP内置对象将信息传给ASP页面,解析执行ASP页面并返回HTML。
   实际上,与CGI技术相比,这是一个极大的改进,但是ASP.NET采取更深层次的途径,引入抽象概念,避免了开发人员必须和底层打交道的局面。
   安装完毕后,ASP.NET 配置IIS将ASP.NET文件重定向到一个新的ISAPI扩展叫aspnet_isapi.dll。这个扩展和以前的asp.dll扩展不同,后者只是负责解析和执行请求的ASP页面。普通的ISAPI模块处理请求的步骤被IIS隐藏,因此ISAPI扩展可能采用不同的模式去处理请求。
   表1:aspnet_isapi.dll的IIS应用程序映射

    除了表1中所列的文件扩展,ASP.NET ISAPI扩展也管理一些不支持浏览器请求的文件扩展,比如Visual Studio工程文件,源代码及配置文件。

ASP.NET处理模型
   目前为止,我们已经知道当ASP.NET文件请求被IIS接收时,它被传至aspnet_isapi.dll,它是ASP.NET相关处理的主入口。实际上,ISAPI 怎样处理请求依赖于操作系统的IIS版本,不同的版本处理模型可能不一样,处理模型由一系列ASP.NET运行时处理请求,产生响应组成的操作序列。
   在IIS 5.X中,所有ASP.NET相关的请求被分派到一个叫aspnet_wp.exe的外部工作进程中。宿主在inetinfo.exe进程中的ASP.NET ISAPI 扩展将控制权交给aspnet_wp.exe,随同所有的请求信息。两者间的通信是通过命名管道的,即大家所熟知的IPC(进程间通讯)机制。ASP.NET worker process和ISAPI扩展一起执行相当数目的任务,它们是ASP.NET请求背后的主要承担者。介绍一下后面会讨论的主题,每个虚拟目录对应的应用程序,都在相同的上下文中执行,即ASP.NET worker process。为了提供执行上下文环境的隔离,ASP.NET模型引入了应用程序域,即AppDomains。可以认为它们是一些轻量级的进程,更多信息后面会讲到。
   在IIS 6中,aspnet_wp.exe进程并未使用,而是另一个叫w3wp.exe的进程。而且inetinfo.exe不再为ISAPI扩展传递HTTP请求,但还是可以处理其他协议的请求。与前一版本IIS的处理模型相比,IIS 6.0改变了许多其他细节,虽然它保持兼容性,模仿先前IIS版本的行为。一个大的改进是,与运行在IIS 5之上的处理模型相比,请求是在较低的内核级处理的,然后传递给相应的ISAPI扩展,从而避免了进程间通讯,从性能和资源消耗的角度来看,进程间通讯代价不菲。接下来我们会深入研究这方面的主题。
   
IIS5.0处理模型
   这是Windows 2000和XP机器默认的处理模型。前面提到它存在于inetinfo.exe进程中,默认监听TCP 80端口进来的HTTP请求,将请求排列成一队列,等待处理。如果请求指定为请求ASP.NET类型时,这个处理被委托给aspnet_isapi.dll。接下来,通过命名管道与ASP.NET worker process aspnet_wp.exe进行通信,最后由worker process 将请求提交给ASP.NET HTTP运行时环境进行处理。详细如图2所示。 
    图2:IIS5.0处理模型

   在图2中提到了我们还未提及的一个元素,ASP.NET HTTP 运行时环境。这不是本文的主题,会在后续文章中给予解释。但由于本次的讨论我们将它视作黑箱,所有ASP.NET相关处理都在这发生,所有的托管代码位于这,开发人员可以处理,从HttpRuntime 到HttpHandler,处理请求,产生响应。它也被叫作ASP.NET管道或HTTP运行时管道。
   
这个处理模型中有意思的一点是,所有的请求,一旦被ISAPI扩展处理并传递给ASP.NET工作进程。同一时间只有一个进程实例是活动的,一个例外,后面会讲到。因此所有宿主在IIS上的ASP.NET web应用程序,实际上是宿主在工作进程中。但是这并不意味着所有的应用程序运行在同一上下文环境中,共享彼此的数据。前面提到,ASP.NET引入了AppDomain的概念,它是一种托管的轻量级的进程,提供隔离及安全边界。每个IIS虚拟目录在独立的AppDomain中执行,当属于应用程序的任何资源第一次请求时,它会被自动加载到工作进程中去。一旦AppDomain被加载,也就是说所有处理请求需要的程序集被加载到AppDomain中,控制权也转移至ASP.NET管道进行实际的处理。多个AppDomain可以运行在同一进程中,同一个AppDomain的请求可以用多个线程来处理。然而线程不属于AppDomain,它可以服务不同的AppDomain请求,但是在给定的时间里一个线程只能属于一个独立的AppDomain。
      出于性能原因,工作进程可能会根据一些标准回收,可以从位于C:\windows\microsoft.net\Framework\
[framework version]\CONFIG目录下的machine.config 文件中得到详细的说明,进程生存同期,请求服务数及请求队列数,空闲时间及内存消耗。一旦到达这些参数的极值,ISAPI扩展创建工程进程的新实例,用它来继续处理请求。此时多个进程实例同时运行。实际上,老的进程实例并未被杀掉,但它被允许中断对未完成请求的服务。


IIS6.0处理模型
   IIS6.0处理模型默认运行在Windows 2003 Server操作系统上。它在IIS 5处理模型上引入了一些新东西,最大的改进要数引入了应用程序池的概念。在IIS 5.X上的所有应用程序,也就是所有的AppDomain都宿主在ASP.NET工作进程中。为了实现更好的安全边界及定制性,IIS 6进程模型允许应用程序运行在不同的工作进程中,w3wp.exe。每个应用程序池可以容纳多个AppDomain,宿主在独立的工作进程中。换句话说,从单个进程运行所有的应用程序转变到每个工作进程运行一个应用程序池。这个模式也被称作工作进程隔离模式。
   另一个较之前的模型大的改变是IIS侦听进来的请求的方式。在IIS 5模型中,它由IIS进程inetinfo.exe对到来的请求通过指定的TCP端口进行侦听的。在IIS 6体系中,通过一个内核级驱动,
http.sys,对到来的请求进行处理和排列,而不是之前的用户模式。这种方式比老的模式有一些优点,称作内核级请求队列。
   图3:IIS6.0处理模型

      图3显现了IIS 6处理模型下参与处理请求的一些关键组件。请求到达时,内核级设备驱动http.sys将它路由到正确的应用程序池队列中。每个队列属于指定的应用程序池,也就是属于特定的工作进程,它从队列中接收请求。这种方式极大的减少了IIS 5中命名管道带来的开销,因为进程间通讯将不再发生,请求通过内核级驱动直接被传递到工作进程中。这有很多优点,包括可靠性。因为运行在内核级模式下,请求分派不受用户模式下工作进程宕机和故障的影响,因此即使工作进程宕机了,系统仍可以接收请求,最终重起宕机的进程.
   工作进程负责加载ASP.NET ISAPI扩展,接下来,加载CLR,最后把所有的任务委派给HTTP运行时。
    w3wp.exe工作进程,不同于IIS 5中的aspnet_wp.exe进程,并非ASP.NET指定,它处理任何类型的请求。然后工作进程根据资源类型决策哪个ISAPI模块被加载并提供服务。
  出于简化的目的,在图3中有一处细节没有标出,到来的请求通过IIS 6中加载的称作Web管理服务(WAS)的模块,从应用程序池队列中传递给正确的工作进程。这个模块负责从IIS元数据中读工作进程,绑定Web应用程序,并将请求引导至正确的工作进程。

参考
 
  • Simone Busoli - ASP.NET Internals - The bridge between ISAPI and Application Domains
  • BrainBell.com – ASP.NET Application Fundamentals
  • Dino Esposito – Programming Microsoft ASP.NET 2.0 Core Reference
  • Dino Esposito – Programming Microsoft ASP.NET 2.0 Applications Advanced Topics
  • George Shepherd – IIS 6.0: New Features Improve Your Web Server's Performance, Reliability, and Scalability
  • Fritz Onion – ASP.NET Pipeline: Use Threads and Build Asynchronous Handlers in Your Server-Side Web Code
  • Tim Ewald and Keith Brown – HTTP Pipelines: Securely Implement Request Processing, Filtering, and Content Redirection with HTTP Pipelines in ASP.NET