ASP.NET HTTP 运行时

  为了加深印象,自己试着翻译了MSDN上The ASP.NET HTTP Runtime这篇文章。 头一次翻译,花了整个周末的时间,整理了一下发现很多句子还是很绕口,万事开头难啊。看来以后要看到好文章就翻译翻译了。提高英语、提高技术、还觉的过的很充实,我这是一箭三雕啊

  PS:很久没有打这么多字了,敲字的感觉不错,嘿嘿。

 

ASP.NET HTTP 运行时

摘要:详细描述了组成Http运行时的组件和不同的请求对应到ASP.NET应用程序的逻辑。以web garden模型和最新的IIS6进程模型的角度查看工作进程的行为。并且演示了从http请求到返回html文本的全过程。

 

内容:
简介
ASP.NET基础设施组件
Web Garden模式
HTTP管道
临时文件和页面程序集
总结

 

简介


无论底层使用哪种平台,可靠性与性能是所有web应用程序的关键。虽然这两点从某种意义上说相互制约。 举例来说,为构建一个可靠的、健壮的应用程序,你可能想将web服务器从具体应用程序中分离出来,应用程序从而工作在进程外。但是与web服务进程的分离,使得仅工作在一个内存上下文的应用程序本质上变慢。出于此原因,应采取任何合理措施确保web服务进程外的代码尽可能快的运行。

微软ASP.NET运行时环境构建时遵循这一设计原则,考虑在可靠性与性能上的提升。结果使ASP.NET进程模型由2个基础体系组成:一个工作在web服务进程内的进程内连接器和一个外部工作进程。 另外,ASP.NET运行时基础设施伸缩性很强,可以自动的在多处理器上任意一个选定的处理器上工作。这个模型就是web garden,允许多个工作进程同时运行在各自制定的处理器上。

总的来说,ASP.NET运行时有3个有效属性
ASP.NET工作进程与应用程序完全解耦,应用程序的生命周期不会影响到服务进程的生命周期。换句话说就是工作进程可以随着应用程序创建和销毁。
虽然ASP.NET应用程序从不在服务进程内工作,但在大多数情况下,总体性能与工作在服务进程内相差不大。
web garden支持内置和可配置。根据配置文件中的设置,工作进程可以通过克隆自己与所有cpu关联。

本文中,我们将检查与复习ASP.NET 运行时环境的组成元素,逐步查看从一个URL请求到返回一个html的曲折道路。

除非明显说明,下面的描述所引用的是ASP.NET默认的进程模型——在微软IIS5.x唯一可用的模型

 

ASP.NET基础设施的组成部分

 

ASP.NET应用程序在web服务器宿主下运行。在微软Windows server平台下,web服务器是IIS下名为inetinfo.exe的可执行程序。它包含在Windows 2000及以后的操作系统中。注意,在Windows Server2003中,IIS和ASP.NET时不会默认安装,必须在控制面板-添加删除程序中安装。

IIS在非托管下执行,并且提供了基于ISAPI扩展块和筛选器模块的扩展模型。通过编写这种模块,编程人员可以直接管理特定类型资源的请求,并且在各种预处理步骤中挂起当前请求。扩展和筛选是一些输出众所周知的签名DLL中的函数。一些插件是在IIS元数据中注册和配置的。

仅有一些类型的资源,客户端请求后可由IIS直接处理。例如一个html页面请求、文本文件、JPEG和GIF图片由IIS处理。请求ASP文件调用一个名为asp.dll的ASP扩展模块。同样,请求一个ASP.NET资源(如*.aspx,*.asmx,*.ashx)经由ASP.NET ISAPI扩展。这个系统组件是一个名为aspnet_isapi.dll的win32 DLL。ASP.NET 扩展处理各种资源类型,包括web服务和http处理程序调用。

ASP.NET ISAPI扩展是一个win32 DLL,并不为托管代码提供宿主。 这是一个接受和分配各种ASP.NET资源请求的中心控制台。按设计这个模块被放在IIS进程中,运行在带有管理员权限的系统帐户下,该账户不能有编程人员和系统管理员修改。ASP.NET ISAPI扩展负责调用ASP.NET工作进程(aspnet_wp.exe)aspnet_wp.exe按序控制执行请求。除了对请求路由外,ASP.NET ISAPI还监视工作进程状况并且负责在性能下降到一定阈值时结束进程。

工作进程是一个小的win32 shell代码,集成CLR并运行托管代码。为aspx、asmx、ashx资源服务。通常一个机器上只有一个这样的进程。所有当前激活的ASP.NET应用程序在里面运行,每一个分布在一个应用程序域中。但在前面提过,工作进程支持web garden模式,这意味着同样的进程副本运行在所有和进程关联的cpu上。(更多内容在后面的“web garden 模式”章节)。

通过使用一组命名管道在ISAPI和工作进程之间的通信。命名管道是一个在进程边界间传输数据的Win32机制。如名所示,命名管道工作起来与管道一样:你在一端输入数据,另一端输出同样的数据。管道可以在本地进程间或运行在另外一个机器上的进程之间建立连接。对本地进程间通信,管道是windows平台上最高效和灵活的工具。

为确保最佳性能,aspnet_isapi使用异步命名管道将请求转发到工作进程并得到响应。另一方面,当需要IIS环境更多信息(即服务器变量)时,工作进程使用同步管道。aspnet_isapi模型创建固定数量的命名管道并且通过一个小的线程池使用重叠操作为并发的链接服务。当一个管道驱动的数据传输操作完成时,断开客户端操作完成,重用管道实例给另一个新链接服务。线程池和回执操作确保了ASP.NET ISAPI的高性能。在任何情况下,ASPNET_ISAPI扩展不会处理HTTP请求。

ASP.NET请求的处理逻辑可以归纳为如下步骤。
1、当请求到达,IIS检查资源类型并调用到ASP.NET ISAPI扩展。如果默认处理模式可用,aspnet_isapi会将请求排队并将其分发到工作进程。任何请求数据通过异步I/O操作发送。如果IIS6进程模式可用,请求被自动排队在应用程序所属的IIS应用程序池的工作进程(w3wp.exe)中。IIS6工作进程不知道任何关于ASP.NET和托管代码的事情。它限于处理*.aspx扩展并加载aspnet_isapi模块。当ASP.NET ISAPI工作在IIS6进程模式下,其行为不同,只是在w3wp.exe工作进程上下文中加载CLR。

2、当接收请求后,ASP.NET工作进程通知ASP.NET ISAPI它将为该请求服务。通知发生在整个同步I/O操作之中。使用同步模块的原因是为了保持使ISAPI内部请求表中未标为“执行中”的请求不会被工作进程处理。一个正处于工作进程服务的请求不能在指配到其它进程,除非原进程结束。

3、在工作进程的上下文中执行请求,有些环境下,工作进程为处理访问服务器变量的请求需要回访ISAPI。这种情况下工作进程使用同步管道,因为需要保持请求处理逻辑顺序。

4、请求完成后,打开一个异步管道将响应发送到aspnet_isapi。请求状态变为“完成”;之后请求将从表中移除。如果工作进程意外结束,进程所处理的所有请求在一段时间内仍然保持“执行中”状态。当aspnet_isapi检测到工作进程结束,它将自动终止请求,释放所有指配的IIS资源。

上述内容提到了ASP.NET默认进程模式:一个建立在IIS5.x上的工作模式。IIS6的默认工作方式同样对ASP.NET处理模式有影响。当集成在IIS6,ASP.NET1.1自动调整工作方式以适应宿主环境。不再使用aspnet_wp工作进程并且一些定义在machine.config文件中的配置参数也被忽略。从ASP.NET的角度来看,IIS6最大的变化是一切与请求相关的东西发生在aspnet_isapi控制下,并且工作在w3wp.exe进程上下文中。工作进程用户帐号与应用程序所属应用程序池的用户帐号相同。默认情况下,这个帐号是NETWORKSERVICE,一个内置、与ASPNET等效的弱功能账户。

工作进程受命为进程回收的功能支配。进程回收存在于aspnet_isapi,当一个进程消耗了大量内存、相应缓慢或者挂起时可以自动开始一个新进程。当此操作发生时,一个新的请求会由一个新实例负责,变为一个新活动进程。尽管如此,所有指派到旧进程的请求依然等待中。当旧进程完成等待请求处于静止状态时,它将结束。如果工作进程意外中止或者停止处理请求,所有等待的请求将被重新分配到新进程中。

尽管ASP.NET ISAPI和工作进程是ASP.NET 运行时基础设施的关键组件,其他的执行也为它们的工作作出贡献,下表是所有这些组件。
名称            类型             帐号
aspnet_isapi.dll     win32 DLL(ISAPI扩展)   LOCAL SYSTEM
aspnet_wp.exe     win32 EXE         ASPNET
aspnet_filter.dll     win32 DLL(ISAPI筛选器)  LOCAL SYSTEM
aspnet_state.exe    win32 NT Service     ASPNET

aspnet_filter.dll组件是一个小Win32 ISAPI筛选器,用于备份ASP.NET应用程序无cookie会话状态。在Windows 2003中,当IIS6进程模式可用时,aspnet_filter.dll还可以过滤对Bin文件夹中的非可执行资源的请求。

aspnet_state.exe的角色在web应用程序中是至关重要的,会话状态管理中必须用到它。有一项可选服务可将会话状态数据保存在web应用程序内存之外。可执行文件是一个NT服务,可以在本地机器或远程运行。当服务激活,可以配置ASP.NET应用将会话信息保存在该进程的内存中。一个相似的配置提供了更高的可靠性,可使数据不受进程生命周期和ASP.NET应用程序失败的影响。该服务运行在ASPNET本地帐号下,可以在服务控制管理器中配置。

还有一些不属于基础设施的部分,另一个应该提到的可执行设施是aspnet_regiis.exe。该工具设置可将不同的ASP.NET版本应用在同一台机器上。该工具对修复IIS和ASP.NET的错误配置十分有用。该工具用于更新存储在IIS元数据根目录下的脚本映射。脚本映射是在资源文件和ASP.NET模块建立关联的集合。最后,该工具可以显示安装的ASP.NET版本状态。用于执行其他配置操作,如授予指定文件夹的NTFS权限和创建客户脚本目录。

 

Web Garden 模式


Web Garden 模式可以通过machine.config文件下的<processModel>节点配置。注意<processModel>节点是唯一一个不能在用在应用程序的web.config文件内配置的节点。这意味着Web Garden 模式适用于在机器上运行的所有应用程序。但是,通过使用machine.config资源的<location>节点,你可以改变机器上单个应用程序的设置。

<processModel>节点下的两个属性可以影响Web garden模式。他们是 webGarden 和cpuMask。webGarden属性通过一个布尔值设置是否允许多个工作进程(每个进程对应关联cpu)使用。该属性默认为false。cpuMask属性存储一个DWORD值,其二进制格式可为cpu提供一个位掩码,指示cpu是否有资格运行ASP.NET工作进程。默认值是-1(0xFFFFFF),以为所有可用的cpu都可用。当webGarden属性设置为false时cpuMask内容被忽略。cpuMask属性还提供了设置aspnet_wp.exe运行的最大数。

谚语“发光的不都是金子”很适合这里。web garden允许多个工作进程同时运行。但是,你应该注意的是所有进程都有应用状态的副本,进程内会话状态、ASP.NET缓存、静态数据和其他需要运行应用程序的东西。当启用web garden模式时,ASP.NET ISAPI创建于cpu数量相同的工作进程,每个都是上份的完整拷贝(每个都与对应的cpu关联)。为平衡工作量,进来的请求已循环的方式分配到运行的进程中。工作进程就像在单处理器中得到回收。请注意,ASP.NET从操作系统继承了任何cpu的使用限制,并且不包含任何自定义语法去这样做。

总而言之,web garden模式对所有应用程序不是必要的。状态多的应用程序会在性能上冒更大的风险。工作数据存储在共享内存中,所以一个进程中的任何改变都会反映到其它进程中。但是,服务一个请求时工作数据被拷贝到进程上下文中。每个工作进程因此都将处理工作数据的副本,越是状态多的应用程序,付出的性能代价越高。这方面,需要绝对准确和机敏的应用程序基准测试。

改变配置文件的<processModel>节点,只有在IIS重启后生效,在IIS6中,web garden参数保存在IIS元数据中,webGarden和cpuMask属性被忽略。

 

Http 管道


当ASP.NET ISAPI扩展启动工作进程时会传入一些命令参数。在加载CLR之前工作进程使用这些参数完成一些需要的工作。参数值包括用于COM和DCOM安全级别的认证;可用命名管道数量和IIS进程ID。命名管道的名称使用IIS进程ID和使用数量随机产生。工作进程没有接受到命名管道的名称,但是接受到的信息足够识别它们。

COM和DCOM安全性与.NET Framework有什么关系呢?实际上,CLR是作为一个COM对象的。更确切的说,CLR本身不是由COM代码组成的,但是CLR的接口是一个COM对象。所以工作进程加载CLR时当作一个COM对象处理。

当一个ASPX请求到达IIS,Web服务器根据认证模型分配一个令牌,该令牌是基于匿名、Windows、基本或分类。当工作进程接受到处理请求时,这个令牌被传到工作进程中。请求被工作进程中的一个线程获取。这个线程从最先处理该请求的IIS线程上继承了身份令牌。在aspnet_wp.exe上下文中,负责处理请求的实际账户依赖于ASP.NET应用程序配置如何模拟的。如果不用模拟(默认设置),线程则运行在工作进程的账户下。默认情况下,这个账户在ASP.NET进程模式下是ASPNET,在IIS6进程模式下是NETWORKSERVICE。两者都是“弱功能”账户,提供了一些功能限制并很好的避免了回归攻击(回归攻击是将客户端模拟的安全标记回复给父进程标记,给定工作进程一个弱功能的帐号可以使此类攻击失败)

高度概括,ASP.NET工作进程完成了一个主要任务,将请求处理给一系列的称为HTTP 管道的托管对象。激活HTTP管道要创建一个新的HttpRuntime类实例,调用ProcessRequest方法。以前提过,ASP.NET只有一个单独的工作进程(除非启动web garden模式),在各自的应用程序域中运行Web应用。每个应用程序域有一个单独的HttpRuntime类的实例——管道的入口点。HttpRuntime对象初始化大量内部对象用于处理请求输出。帮助对象包括缓存管理(Cache对象)和用于发现应用程序源文件改变的内部文件系统监控器。HttpRuntime对象为请求创建上下文,将请求用到的各种HTTP信息填充在上下文中。上下文表示为一个HttpContext类实例。

另一个在Http runtime初始期创建的辅助对象是文本书写器,包含要给浏览器输出的文本。文本书写器是一个HttpWriter类的实例,对象实际上缓存了任何代码页输出的编程文本。一旦HTTP runtime初始化完毕,它将查找处理请求的应用程序对象。一个应用程序对象是一个HttpApplication类的实例,该类隐藏在global.asax文件中。global.asax在编程上是可选的,在基础设施上是必须的。因此,当应用程序中没有类创建时,必须创建一个默认对象,ASP.NET 运行时由一些预先用于查找和返回有效服务请求的中介工厂类组成。第一个使用到的是HttpApplicatinFactory工厂类。其主要任务是使用URL信息去将URL虚拟目录和对象池中HttpApplication对象匹配。


该类的行为如下所述:
1、该工厂类维护一个用于服务应用程序请求的HttpApplication对象池。该对象池与应用程序拥有相同的生命周期。
2、当应用程序第一个请求到达时,该工厂类提取应用有关该程序类型的信息(global.asax类),设置监控更改的文件,创建应用程序状态,触发Application_OnStart事件。
3、该工厂从对象池中取得一个HttpApplication实例并且将其用于处理请求。如果没有可用对象,则创建一个新的HttpApplication对象。创建的HttpApplication对象使用了编译的global.asax应用程序文件。
4、HttpApplication开始处理请求,在处理完成前不会在用于处理新的请求。当同样资源的新请求再次进入时将有对象池中其他对象处理。
5、应用程序对象给所有注册的HTTP模块一个预处理该请求的机会,并找出那种处理类型最适合处理该请求。使用查找请求URL的扩展名和配置文件信息来做这些事情。

Http处理程序是一些实现了IHttpHandler接口的类。.NET Framework提供了一些预定义的处理程序用于通用资源类型。包括ASPX页面和Web服务。machine.config文件的<httpHandlers>节点定义了用于处理特殊资源的要实例化的HttpApplication对象类名。如果帮助类是一个处理程序工厂,那么GetHandler方法将最终决定使用处理程序的类型。这时,将从相似对象池中获取一个适当类型的处理程序并对其进行配置用于处理请求。

IHttpHandler接口提供了两个方法:IsReusable和ProcessRequest。前面的方法返回一个布尔值,用于显示处理程序能否被对象池使用。(大多数预定义的处理程序可以在对象池中,但你可以自定义每次请求都需要一个新的实例)。ProcessRequest方法包括所有处理一个指定类型资源的逻辑。例如,ASPX页面处理程序基于下列伪码。
private void ProcessRequest()
{
   // 判断请求是否是postback
IsPostBack = DeterminePostBackMode();

// 触发aspx代码页的PageInit事件
PageInit();

//加载ViewState,处理已发送的值
if (IsPostBack) {
   LoadPageViewState();
      ProcessPostData();
}  

//触发aspx代码页的PageLoad事件
PageLoad();

// 1)再次处理已发送值 (当动态创建控件时)
// 2)将属性更改为运行在服务端的事件提示为事件驱动控件(如,checkbox状态改变)
// 3)执行与postback事件相关的所有代码
if (IsPostBack) {
      ProcessPostDataSecondTry();
   RaiseChangedEvents();
      RaisePostBackEvent();
}  

//触发aspx代码页的Page_PreRender事件
PreRender();

//将当前控件状态保存在viewstate中
SavePageViewState();

//将页面内容呈现给HTML
RenderControl(CreateHtmlTextWriter(Response.Output));
}

不管它们调用哪种资源类型,基于HTTP处理程序的模型都是相同的。随资源类型变化的只有处理程序。HttpApplication对象负责找出哪种处理程序可以处理当前请求。HttpApplication对象还负责检测动态创建的、呈现资源的程序集(如一个.aspx页面或者一个.asmx web服务),如果发现任何改变,应用程序对象确保编译并加载请求资源的最新来源。

 

临时文件和页面程序集

 

在完成ASP.NET HTTP运行时之旅前,一起来分析一下当一个ASP.NET页面请求时文件系统发生了什么。你即将看到,HTTP管道将管理和监视一大批临时动态创建的文件。

当你编写和配置一个.aspx文件的网页时,你可以将核心代码隔离到C#或VB .net代码隐藏类中。对于显示为URL的页面来说,文件必须随时在应用程序网站空间中可用。 .aspx文件实际内容决定应用程序对象加载的程序集。

设计时,HttpApplication对象查找一个按请求ASPX文件命名的类。如果页面名为sample.aspx,那么对应加载的类名为ASP.sample_aspx。应用程序对象查找网站程序所有程序集文件夹,包括全局程序集缓存文件夹,Bin子文件夹和ASP.NET临时文件夹。如果没有找到,HTTP基础架构分析.aspx文件的源代码,创建一个C#或VB .net类(根据.aspx页语言设置),并实时编译。新创建的程序集随机命名,放在应用程序特殊子文件夹 C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\Temporary ASP.NET Files目录下。

v1.1.4322子文件夹为ASP.NET1.1所用;如想查看不同版本型号,如果你在使用ASP.NET1.0,子文件夹名为v1.0.3705.下次页面被访问时,程序集已存在并不会被再次创建。但是HttpApplication对象是如何知道一个特殊页面的程序集是否存在呢?他是否每次都要扫描一堆文件夹呢?当然不是这样。

应用程序对象仅查看ASP.NET临时文件夹下的内容。HttpRuntime.CodegenDir属性返回应用程序指定的具体路径。如果.aspx文件第一次被访问(就是说页面程序集尚未创建),这个文件夹中还没有一个已该ASPX页面名称开头的XML文件。例如,sample.aspx页面和动态程序集关联,应包含下面名称的入口点:
sample.aspx.XXXXX.xml

XXXXX占位符是散列值。通过阅读XML文件内容,应用程序对象知道了相应的程序集名称和其中要找的类。下面一小段代码是这种帮助文件的典型内容。包含ASP.sample_aspx类的程序集名称是mvxvxxr.
<preserve assem="mvxvx8xr" type="ASP.sample_aspx">
    <filedep name="c:\inetpub\wwwroot\vdir\sample.aspx" />
</preserve>
不用说就知道这个文件是在源代码文件分析为动态程序集时创建的。任何源文件的改变将使程序集作废并且创建下次请求时会引发一次重新编译的过程。你应该注意到这个执行细节可能会在将来的ASP.NET framework中发生重大变化。任何原因使得你在应用程序使用它时都要非常小心。

当程序集由于一个页面更新而重新创建时,ASP.NET验证旧的程序集是否可以删除。如果程序集仅包含了修改页面的类,ASP.NET尝试删除并替换程序集;否则新创建的程序集将不会影响旧程序集。

在删除过程期间,ASP.NET将找到该程序集文件是否加载并锁定。这种情况下,旧程序集将重命名,添加一个.DELETE扩展名。(注意所有的Windows文件在使用时都允许重命名)这些.DELETE临时文件将在应用程序重启时移除,举例来说,因为一个应用程序文件的改变,如global.asax和web.config。在任何情况下,ASP.NET运行时在服务下一个请求时不会移除这些文件。

注意到默认情况下,在应用程序重启前,ASP.NET应用程序允许最多15个页面重编译,同时损失一些会话和应用程序数据。当最后一此编译超过了 <httpRuntime>节点numRecompilesBeforeAppRestart 属性设置的极限,应用程序域被卸载,应用程序重新启动。同时注意,.NET Framework中你不能卸载单个程序集。应用程序域是CLR下最小代码单元。

 

总结


ASP.NET 应用程序有两个相关特性:进程模型和页面对象模型。ASP.NET提前用到了IIS6.0的一些特性,IIS6.0是Windows Server 2003下的一个新的、革命性的微软网站信息服务版本。特别的是ASP.NET应用程序运行在分开的工作进程中,就像所有在IIS6的应用程序一样。而且,ASP.NET运行时自动回收工作进程已确保高性能,尽管运行时会有异常,内存溢出和编程错误。这种特性已成为IIS6.0的系统特性。

这篇文章中,我介绍了默认ASP.NET进程模型的基础知识和IIS级别代码(ASP.NET ISAPI扩展)和工作进程的交互。同时,我还试着指出其与IIS6进程模型的区别。虽然我没有充分的讲解页面对象模型,那是我以后的文章将要讲到的东西。请继续关注!

学习更多ASP.NET,HTTP运行时和页面对象模型内部知识,请参考我的新书Microsoft Press出版的Programming Microsoft ASP.NET。

 

关于作者

Dino Esposito是一位来自意大利罗马的讲师和顾问,是Wintellent团队的成员,Dino擅长ASP.NET和ADO.NET,并且花费大部分时间往返于欧洲和美国讲学和咨询。特别的是,Dino负责Wintellect的ADO.NET课件并为MSDN杂志写“前沿”专栏。使用dinoe@wintellect.com取得联系。

posted @ 2010-03-21 23:31  binfen  阅读(1674)  评论(0编辑  收藏  举报