三HTTP 管道
ASPNET ISAPI 扩展启动辅助进程后它将传递部分命令行参数辅助进程使用这些参数来执行加载 CLR 前需要执行的任务传递的值包括COM 和 DCOM 安全性所要求的身份验证等级可以使用的命名管道的数量和 IIS 进程标识命名管道的名称是使用 IIS 进程标识和允许的管道数随机生成的辅助进程不接收可用管道的名称但可以接收识别管道名称所需的信息
COM 和 DCOM 安全性与 Microsoft? NET Framework 有何关系?实际上CLR 是作为 COM 对象提供的更准确地说CLR 本身不是由 COM 代码构成的但是指向 CLR 的接口却是一个 COM 对象因此辅助进程加载 CLR 的方式与加载 COM 对象的方式相同
当 ASPX 请求遇到 IIS 时Web 服务器将根据选择的身份验证模型(匿名WindowsBasic 或 Digest)来分配一个令牌当辅助进程收到要处理的请求时令牌被传递到辅助进程请求由辅助进程中的线程获取该线程从最初获取传入请求的 IIS 线程继承身份令牌在 aspnet_wpexe 中负责处理请求的实际帐户取决于在特殊的 ASPNET 应用程序中是如何配置模拟的如果模拟被禁用(默认设置)则线程将在辅助进程的帐户下运行默认情况下该帐户在 ASPNET 进程模型中为 ASPNET在 IIS 进程模型中为 NETWORKSERVICE这两个帐户都是弱帐户提供的功能比较有限可以有效抵挡回复性攻击 (Reverttoself Attack)(回复性攻击是指将模拟的客户端的安全性令牌回复到父进程令牌为辅助进程分配弱帐户可以挫败此类攻击)
高度概括起来ASPNET 辅助进程完成的一项主要任务就是将请求交给一系列称为的 HTTP 管道的托管对象要激活 HTTP 管道可以创建一个 HttpRuntime 类的新实例然后调用其 ProcessRequest 方法如前所述ASPNET 中始终只运行一个辅助进程(除非启用了 Web Garden 模型)该进程在独立的 AppDomain 中管理所有的 Web 应用程序每个 AppDomain 都有自己的 HttpRuntime 类实例即管道中的输入点HttpRuntime 对象初始化一系列有助于实现请求的内部对象Helper 对象包括缓存管理器(Cache 对象)和内部文件系统监视器(用于检测构成应用程序的源文件的更改)HttpRuntime 为请求创建上下文并用与请求相关的 HTTP 信息填充上下文上下文用 HttpContext 类的实例来表示
另一个在 HTTP 运行时的设置初期创建的 Helper 对象是文本书写器用于包含浏览器的响应文本文本书写器是 HttpWriter 类的实例此对象对页面代码以编程方式发送的文本进行缓存HTTP 运行时被初始化后它将查找实现请求的应用程序对象应用程序对象是 HttpApplication 类的实例该类就是 globalasax 文件背后的类globalasax 在编程时是可选的但在构建结构时是必需的因此如果应用程序中没有构建类则必须使用默认对象ASPNET 运行时包括几个中间工厂类可以用来查找并返回有效的 Handler 对象以处理请求整个过程中用到的第一个工厂类是 HttpApplicationFactory它的主要任务是使用 URL 信息来查找 URL 虚拟目录和汇集的 HttpApplication 对象之间的匹配关系
应用程序工厂类的行为可以概括为以下几点
工厂类维护 HttpApplication 对象池并使用它们来处理应用程序的请求池的寿命与应用程序的寿命相同
应用程序的第一个请求到达时工厂类提取有关应用程序类型的信息(globalasax 类)设置用于监视更改的文件创建应用程序状态并触发 Application_OnStart 事件
工厂类从池中获取一个 HttpApplication 实例并将要处理的请求放入实例中如果没有可用的对象则创建一个新的 HttpApplication 对象要创建 HttpApplication 对象需要先完成 globalasax 应用程序文件的编译
HttpApplication 开始处理请求并且只能在完成这个请求后才能处理新的请求如果收到来自同一资源的新请求则由池中的其他对象来处理
应用程序对象允许所有注册的 HTTP 模块对请求进行预处理并找出最适合处理请求的处理程序类型这通过查找请求的 URL 的扩展和配置文件中的信息来完成
HTTP 处理程序是一些实现 IHttpHandler 接口的类NET Framework 为常见的资源类型提供了一些预定义的处理程序包括 ASPX 页面和 Web 服务machineconfig 文件中的 <httpHandlers> 部分定义了 HttpApplication 对象必须实例化才能处理特定类型资源的请求的类名如果 Helper 类是一个处理程序工厂GetHandler 方法将确定要使用的处理程序类型这时将从一组类似的对象中获取适当类型的处理程序并对其进行配置以处理请求
IHttpHandler 接口提供了两个方法IsReusable 和 ProcessRequest前者将返回一个布尔值表示处理程序是否可以被汇集(大多数预定义的处理程序都是汇集的但是您可以自行定义每次都需要新实例的处理程序)ProcessRequest 方法包含处理特定类型资源所需的所有逻辑例如ASPX 页面的处理程序基于以下伪代码
private void ProcessRequest()
{
// 确定请求是否是回发 (postback)
IsPostBack = DeterminePostBackMode();
// 触发 ASPX 源代码的 Page_Init 事件
PageInit();
// 加载 ViewState处理已发送的值
if (IsPostBack) {
LoadPageViewState();
ProcessPostData();
}
// 触发 ASPX 源代码的 Page_Load 事件
PageLoad();
// ) 再次处理已发送的值(当
// 动态创建控件时)
// ) 将属性更改的服务器端事件提升为输入驱动的
// 控件(即复选框的状态改变)
// ) 执行与回发事件相关的所有代码
if (IsPostBack) {
ProcessPostDataSecondTry();
RaiseChangedEvents();
RaisePostBackEvent();
}
// 触发 ASPX 源代码的 Page_PreRender 事件
PreRender();
// 将控件的当前状态保存到 ViewState 中
SavePageViewState();
// 将页面内容呈现给 HTML
RenderControl(CreateHtmlTextWriter(ResponseOutput));
}
无论调用的资源类型如何基于 HTTP 处理程序的模型是相同的唯一随资源类型变化而变化的元素是处理程序HttpApplication 对象负责查找应该使用哪种处理程序来处理请求HttpApplication 对象还负责检测对动态创建的表示资源的程序集(如 aspx 页面或 asmx Web 服务)所进行的更改如果检测到更改应用程序对象将确保编译并加载所请求的资源的最新来源
四临时文件和页面程序集
要全面了解 ASPNET HTTP 运行时让我们来分析一下当请求 ASPNET 页面时文件系统层所发生的变化接下来您将了解由 HTTP 管道的对象管理和监视的一组动态创建的临时文件
虽然可以将页面的核心代码隔离在代码背后的 C# 或 Microsoft? Visual Basic? NET 类中但可以将 Web 页面编写和部署为 aspx 文本文件对于要显示为 URL 的页面来说aspx 文件在应用程序的 Web 空间中必须始终可用aspx 文件的实际内容将确定应用程序对象要加载的程序集(或多个程序集)
按照设计HttpApplication 对象将查找一个根据请求的 ASPX 文件命名的类如果页面命名为 sampleaspx则要加载的相应的类名为 ASPsample_aspx应用程序对象在 Web 应用程序的所有程序集文件夹中查找这样的类这些文件夹包括全局程序集缓存 (GAC)Bin 子文件夹和 Temporary ASPNET Files 文件夹如果未找到这样的类HTTP 结构将分析 aspx 文件的源代码创建一个 C# 或 Visual Basic NET 类(具体创建哪种类取决于 aspx 页面上设置的语言)同时对其进行编译新创建的程序集的名称是随机生成的位于特定于应用程序的子文件夹中路径如下所示 C:\WINDOWS\MicrosoftNET\Framework\v\Temporary ASPNET Files
子文件夹 v 特定于 ASPNET 如果您使用的是 ASPNET 子文件夹的版本号会有所不同即子文件夹名为 v再次访问页面时程序集就已存在不需要重新创建但是HttpApplication 对象是如何确定特定于页面的程序集是否存在呢?它每次都要扫描大量文件夹吗?不并不是这样
应用程序对象只查看 Temporary ASPNET Files 文件夹中某个特殊文件夹的内容具体路径(特定于应用程序的路径)由 HttpRuntimeCodegenDir 属性返回如果是第一次访问 aspx 文件(即还未创建页面程序集)则该文件夹中就不存在以 ASPX 页面名称开头的 XML 文件例如具有动态程序集的 sampleaspx 页面应有如下的条目
sampleaspxXXXXXxml
XXXXX 占位符是一种散列代码通过读取该 XML 文件的内容应用程序对象就可以了解要加载的程序集的名称以及要在其中获取的类以下代码片段是这种 Helper 文件的典型内容包含 ASPsample_aspx 类的程序集的名称是 mvxvxxr
<preserve assem=mvxvxxr type=ASPsample_aspx>
<filedep name=c:\inetpub\wwwroot\vdir\sampleaspx />
</preserve>
当然只有在分析 filedep 文件的源代码以生成动态程序集时才创建该文件对 filedep 文件所做的任何更改都会使程序集无效在下一次请求时必须重新编译需要注意的是在 ASPNET 架构的未来版本中该实现过程可能会有较大改变不论什么原因只要您决定在当前应用程序中使用它都必须十分小心
由于更新而要为页面创建新的程序集时ASPNET 将验证是否可以删除旧的程序集如果旧的程序集只包含修改后的页面的类ASPNET 将试图删除并替换该程序集否则将在保留旧程序集的情况下创建一个新程序集
在删除过程中ASPNET 可能会发现程序集文件已被加载并锁定这种情况下可以为旧程序集添加一个DELETE扩展名以将其重新命名(注意所有 Windows 文件都可以在使用过程中重新命名)只要应用程序重新启动(例如由于对某个应用程序文件如 globalasax 和 webconfig 进行了更改)这些临时的 DELETE 文件就将被删除但在处理下一个请求时ASPNET 运行时不会删除这些文件
请注意默认情况下在整个应用程序重新启动之前每个 ASPNET 应用程序最多可以重新编译 个页面同时会损失一些会话和应用程序数据当最近的编译次数超过了 <httpRuntime> 部分的 numRecompilesBeforeAppRestart 属性中设置的阈值时将卸载 AppDomain并重新启动应用程序还要注意在 NET Framework 中您无法卸载单个程序集AppDomain 是可以从 CLR 卸载的最小的代码块
五小结
ASPNET 应用程序有两大特征进程模型和页面对象模型ASPNET 提前使用了 IIS 的一些功能而 IIS 则是 Windows Server 中提供的全新的开创性的 Microsoft Web 信息服务尤其值得一提的是在独立的辅助进程中运行的 ASPNET 应用程序其行为与 IIS 中的所有应用程序相同而且尽管会出现运行时异常内存洩露或程序错误ASPNET 运行时仍能自动回收辅助进程以保证实现卓越的性能这种功能已成为 IIS 的系统功能
在本文中我概括介绍了默认的 ASPNET 进程模型的基础知识以及 IIS 级代码(ASPNET ISAPI 扩展)和辅助进程之间的交互同时还介绍了与 IIS 进程模型之间的最新区别