ASP.NET内幕 - IIS处理模型
 

 

 

微软的Active ServerPages,即ASP,自1996年首次发布以来,为Web开发者构建Web应用提供了一个丰富、复杂的框架。过去的几年它的基础架构发展的如此迅速,成为目前大家了解的ASP.NET,已经不再象它的前身。ASP.NET是构建Web应用的框架,就是说应用程序运行在Web上,客户-服务器端模式表现为浏览器向Web服务器发送各种资源的请求。象CGI、PHP、JSP、ASP等动态服务器端资源生成技术出现以前,所有Web服务器必须接受客户端静态资源请求,将其发送给请求者。随着动态技术的发展,Web服务器开始承担更多的职责,因为它们必须采取一些方式在服务器端生成动态资源,将结果返回给客户端,这已经是一项不同于以前的任务。

从菜鸟的角度来看,客户端和服务器端的交互很简单,就是出现了使用HTTP(超文本传输协议)的Web通讯。HTTP是一种依赖TCP、IP,在异构网络,即万维网的两个连接节点间传输数据的应用层协议。

每一种动态服务器端技术都完全了解特定的Web服务器实现,ASP.NET跟微软的因特网信息服务器,即IIS紧密结合在一起。

不同的服务器使用不同的方法生成动态资源,我们需要检验的是,IIS怎样在服务器上处理某个请求的路径,将结果返回给客户端。

IIS和ISAPI扩展

前面提到,静态资源不需要服务器处理,一旦接收到静态资源请求,服务器只需要从文件系统获取它的内容,按照HTTP协议向客户端发送内容的字节流。静态资源可以是图片、脚本文件、css样式表,或者html页面。很明显服务器需要知道怎样区别静态和动态资源,因为后者需要经过某些处理,而不是直接将其发送给客户端。这就是ISAPI出现的原因,ISAPI表示因特网应用程序接口(Internet Server ApplicationProgramming Interface)。ISAPI扩展是使用Win32.dll实现的模块,IIS使用它处理特定的资源。ISAPI扩展和文件之间的映射通过IIS插件配置,存储在IIS元数据中。每一个文件扩展可以和特定的ISAPI扩展关联,就是说,当这种文件的请求到达后,IIS将它交给相应的ISAPI扩展,认为它能够处理。

图1:在IIS 5.0中配置ISAPI扩展映射

很明显ISAPI需要实现一个公共接口,IIS能够使用详细描述请求的数据进行调用,生成响应。

如图1的演示,.asp扩展被映射到asp.dll的ISAPI扩展。对于ASP页面,这个组建负责处理所有的请求任务,生成输出,包括收集请求信息,通过 Request、Response以及其它公共ASP内置对象将这些信息传递给ASP页面,解析执行ASP页面,返回结果HTML。

实际上与CGI这样的技术相比,这是一个极大的改进,但ASP.NET采取了更进一步的做法,引入抽象方法,避免开发者必须关注这个过程中到底发生了什么。

安装完后,ASP.NET配置IIS,将特定的ASP.NET文件请求重定向到一个叫做aspnet_isapi.dll的ISAPI扩展。这个ISAPI 扩展的处理跟之前的asp.dll扩展有点不一样,asp.dll扩展只是负责解析和执行请求的ASP页面。普通ISAPI模块处理请求的步骤完全被 IIS隐藏,因此为了处理请求,ISAPI扩展可以采用不同的模式。

表1:aspnet_isapi.dll的IIS应用程序映射

扩展名 资源类型
.asax ASP.NET application files. Usually global.asax.
.ascx ASP.NET user control files.
.ashx HTTP handlers, the managed counterpart of ISAPI extensions.
.asmx ASP.NET web services.
.aspx ASP.NET web pages.
.axd ASP.NET internal HTTP handlers.

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

ASP.NET处理模型

目前为止我们已经了解,IIS接收到ASP.NET文件请求时,将其传给aspnet_isapi.dll这个ASP.NET相关处理的主入口点。实际上 ISAPI扩展怎样进行处理,依赖于操作系统的IIS版本,不同版本的处理模型可能很不一样。处理模型指ASP.NET运行时为了处理请求、生成响应而进行的操作序列。

当运行在IIS5.x时,所有ASP.NET相关的请求被ISAPI扩展分发到一个叫做aspnet_wp.exe的外部工作进程。运行在IIS进程inetinfo.exe中的ASP.NETISAPI扩展将控制转给aspnet_wp.exe,并传递请求相关的所有信息。它们之间的通讯通过命名管道进行,即众所周知的IPC(进程间通讯Inter ProcessCommunication)机制。ASP.NET工作进程与ISAPI扩展一起,执行一定数量的任务,它们是ASP.NET请求背后处理的主要承担者。介绍一下后面会讨论的一个主题,注意对应于IIS上不同虚拟目录的每一个web应用程序,都是在同一个进程的上下文中执行的,即 ASP.NET工作进程。为了在执行上下文中提供隔离,ASP.NET模型引入了应用程序域的概念,简写为AppDomain。它们可以看作是轻量级的进程,更多的讨论在后面。

另一方面如果运行在IIS6,不会使用aspnet_wp.exe,而是另外一个叫做w3wp.exe的进程。另外,inetinfo.exe不再向ISAPI传递HTTP请求,但它仍然处理其他请求协议。尽管IIS 6能够以兼容模式运行,模拟前一个版本IIS的行为,但与IIS5相比处理模型上很多细节改变了。一个大的改进是,与运行在IIS5上的处理模型相比,到达的请求将在较低的内核层级处理,然后传递给适当的ISAPI扩展。这避免了进程间通讯技术,从性能和资源消耗角度来看,进程间通讯是一个昂贵的操作。我们在下面的章节中深入探讨这个。

IIS 5.0处理模型

这是Windows 2000和XP机器上默认的处理模型。前面提到它位于IIS inetinfo.exe进程中,默认监听TCP80端口接收HTTP请求,将请求排列到一个队列中,等待接受处理。如果请求是ASP.NET类型,这个处理被委托给ASP.NETISAPI扩展,即aspnet_isapi.dll。接下来通过命名管道与ASP.NET工作进程 aspnet_wp.exe通讯,最后由工作进程将请求交给ASP.NET HTTP运行时环境。图2以图形方式展现这个处理过程。

图2:IIS 5.0处理模型

图 2中出现了一个我们还没有提到过的额外元素,ASP.NETHTTP运行时环境。这不是本文的主题,会在后续的文章中详细解释,但为了本文的讨论,可以把 HTTP运行时环境看作一个黑箱,ASP.NET相关的处理在这儿完成,所有托管代码位于这,开发者可以在这进行处理,从直接的HttpRuntime到 HttpHandler等,在这处理请求,生成响应。这也被称为ASP.NET管道,或者HTTP运行时管道。

这个处理模型中有意思的一点是,所有的请求一旦被ISAPI扩展处理,就被传给ASP.NET工作进程。同一时间只有一个ASP.NET进程实例是活动状态,不过有一个例外,后面会讨论。因此运行在IIS中的所有ASP.NETweb应用程序实际上也是运行在工作进程上的,然而这并不意味着所有的应用程序运行在相同的上下文中,共享所有的数据。前面提到,ASP.NET引入了AppDomain的概念,它其实是一种托管的轻量级进程,提供隔离和安全边界。每个IIS虚拟目录在独立的AppDomain中执行,当属于这个应用的任何一个资源在第一次请求时,这个AppDomain被自动加载到工作进程中。AppDomain被加载之后,就是说所有用于处理这个请求所需的程序集被加载到AppDomain中,然后控制权被传给ASP.NET管道进行实际的处理。多个AppDomain 可以运行在同一个进程中,同一个AppDomain的请求可以用多个线程来处理,然而线程并不属于AppDomain,它可以服务于不同 AppDomain的请求,但在给定的时间一个线程只会属于一个AppDomain。

出于性能原因,工作进程可能会依据某些条件进行回收,可以从位于C:\windows\microsoft.net\Framework\[frameworkversion]\CONFIG目录中的 machine.config文件看到这些描述性的条件,包括进程生存时间、正在处理以及队列中的请求数、空闲时间以及内存消耗。一旦达到这些参数中的某个预设值,ISAPI扩展为工作进程创建一个新的实例,用它继续处理请求。只有在这个时候多个工作进程并发运行,实际上老的进程实例并没有被结束掉,允许它处理剩余的请求。

IIS 6.0处理模型

IIS 6处理模型是运行Windows 2003 Server操作系统的机器上默认的模型,它在IIS5处理模型的基础上引入了几个改进,最大的改变之一是引入了应用程序池的概念。在IIS5.x上,所有的web应用程序,即所有的AppDomain运行在ASP.NET工作进程中,为了实现更好的安全边界粒度以及定制能力,IIS6处理模式允许应用程序运行在不同的工作进程中,即w3wp.exe。每个应用程序池可以包含多个AppDomain,运行在独立的工作进程中,换句话说就是从单个进程运行所有的应用程序切换到每个工作进程运行一个应用程序池。这个模式也叫做工作进程隔离模式(worker processisolation mode)。

与之前的模式相比,另一个大的改变是IIS监听请求的方式。IIS5模式中,由IIS进程inetinfo.exe来监听特定的TCP端口,接收HTTP请求,在IIS6架构中,通过一个叫做http.sys的内核驱动,在内核层级而不是之前的用户模式下处理和排队请求,这种方式比老的模式有一些优点,叫做内核级请求队列(kernel-level request queuing)。

图3:IIS 6.0处理模型

图 3演示了使用IIS6模式处理请求的主要组建。请求到达后,内核级的设备驱动http.sys将其传递到正确的应用程序池队列中,每个队列属于特定的应用程序池,也就是说属于特定的工作进程,接下来工作进程从队列中接收请求。这种方式极大的降低了IIS5模式中引入的命名管道的开销,因为不再需要进程间通讯,请求直接从内核级驱动传递给工作进程,这样有很多的优点,例如可靠性。因为运行在内核模式中,请求分发不受用户模式,即工作进程中宕机和故障的影响,因此即使工作进程宕机了,系统仍然能够接收请求,重起宕机的进程。

工作进程负责加载ASP.NET ISAPI扩展,它依次加载CLR,将所有的工作委托给HTTP运行时。

w3wp.exe工作进程不同于IIS 5模式中的aspnet_wp.exe进程,跟ASP.NET不相关,它用于处理任何类型的请求,然后工作进程根据它需要处理的资源类型决定加载哪些ISAPI模块。

出于简化目的在图3中有一个细节没有标出来,到达的请求通过IIS 6中加载的一个叫做Web管理服务(WAS)的模块,从应用程序池队列传递给正确的工作进程。这个模块负责从IIS元数据读取工作进程与web应用程序的邦定信息,将请求传递给正确的工作进程。