当访问者的浏览器连接至IIS后,IIS会根据访问者所要求的文件扩展名来启动对应的IIS Extension程序处理访问者的请求,这个文件扩展名列表可以通过IIS所提供的Internet Administrator程序来修改。
t通过查看扩展名列表可知,扩展名为.aspx的网页文件所对应的ISAPI Extension是aspnet_isap.dll,这个文件位于.NET Framework的安装目录中,预设是Windows\Microsoft .NET\Framework\v1(2).xxxxl.Aspnet_isapu.dll在此处作为程序的入口,实际上,处理访问者请求的是其背后的aspnet_wp.exe,.NET将这个程序通称为ASP.NET Worker Proccess(以下简称WP),此文件也同样位于.NET Framework安装目录中。当aspnet_isapi.dll收到IIS所转交的访问者请求时,会将此要求通过named pipe协议转送给运行中的WP程序,此时WP会解析出请求中的虚拟目录信息来决定创建一个新的AppDomain(应用程序域)对象还是使用先前已创建好的AppDomain对象来处理此要求,理论上每一个虚拟目录都对应着一个独立的AppDomain对象,让系统容纳更多的访问者。
当WP发现请求的文件所在的虚拟目录不存在有对应的AppDomain对象时,此时,WP会创建一个新的AppDomain对象,然后加载ASP.NET执行时期所需要的Assemblys及访问者所请求的文件,也就是.aspx文件,
在载入.aspx文件的动作中隐含很多的技术,限于技术水平有限,了解的也不是很多,不过以后会有文章介绍。WP接到来自aspnet_isapi.dll的请求后,会将请求转送至虚拟目录对应的Application Domain中的ISAPIRuntime对象,在解析出必要的信息后,ISAPIRuntime对象,接着调用HttpRuntime.ProccessRequest函数来处理用户要求,此函数中会创建一个HttpContext对象,HttpRuntime对象会将取自ISAPIRuntime对象中的访问者信息通知该对象。总的来说,HttpRuntime对象管理者HttpContext对象则管理着一个HttpSession对象,每一个访问者都对应着一个HttpContext对象,因此每一个访问者自然就拥有独立的HttpSession对象了。在创建HttpContext对象后,随之被创建的时HttpApplication对象,此对象时经由HttpApplicationFactory对象创建的,当HttpRuntime对象向HttpApplicationFactory对象要求HttpApplication对象时,HttpApplicationFactory对象会先解析目录中的Global.asax文件,接着加载虚拟目录内的ApplicationAssembly(Global.dll),而后合并两者创建出一个Ghost Application Class,最后编译Class后取得对象实例后返回至HttpRuntime对象,这个对象实例就是HttpApplication对象。解析与编译.asax文件的动作只发生于此虚拟目录第一次处理用户要求,或是Global.asax、Global.dll在前次执行后又做了变动时。取得HttpApplication对象实例后,HttpRuntime对象接着调用HttpApplication.PreccessRequest函数,此函数会将执行权转交至对应的HttpHandler对象上,我在后面的文章会详细介绍HttpHandler的动作。初始化HttpApplication对象期间,相关的HttpModules会被加载,HttpModules指的是Application层级的模块,比如Session、Authentication等模块,每个HttpModule都必须实现IHttpModule接口,它的原型是:
namespace System.Web
{
Public interface IHttpmodule
{
void Init(HttpApplication context);
void Dispose();(拥有IDisposable接口,采用dispose模式销毁对象)
}
}
其中的Init函数会在HttpApplication对象加载该模块时调用,一般实现中通常会在此函数中挂载时间处理函数至HttpApplication对象所提供的事件上,借此取得这些时间发生时的主导权。Dispose函数则是在HttpModule被释放时被调用,这里通常是撰写释放非托管内存资源的程序代码。
一般设计人员撰写HttpModule的情况不多,多半是用于提供额外的认证或是服务性质的等功能,本文所描述的大部分动作无法介入的,是指难以得知其设计的运作模式,Ghost Application Class 的创建过程可能与设计人员的更关系密切一些,这是ASP.NET如何结合解释和编译技术的关键
此模式中有两个重要角色:分析器(Parser)和编译器(Complier),分析器负责解释应用程序脚本(Global.asax),当使用Code Behind技术是,分析器会加载预编译的Assembly,也就是Global.dll,并使用反射技术取出其中的运行时类型信息,接着创建一个继承此类型的代码,最后调用编译器来编译此代码,然后取得对象实例。另一个与设计人员关联性比较少的技术是Pooling,如前所述,HttpApplicationFactory对象具备了Pooling能力,即当某个HttpApplication对象完成用户请求后,并不会立即释放,而是被保存在一个State对象中,当系统内存资源紧张或是相关的文件发生变更时(Global.asax、Global.dll),这个对象才会真正地被释放,在这之前这些对象实例都是可用的。同样Pooling技术也应用在分析器中,就一般情况来说,假如相关文件没有变动就不需要重新执行解析和编译工作因此分析器引入了分析缓存(ParserCache)技术,当分析器解析编译(Global.asax)后会将结果放入ParserCache对象中,并设定其过期条件为“文件变更”,这样就能避免无意义的解释编译工作。整个ASP.NET中Factory、Parser以及Compiler手法随处可见,我们所熟悉的Global.asax、Page、User Control、Template Control都是采用这种手法处理的。
在结束之前我们先弄清ISAPIRuntime、HttpRuntime、HttpApplication三个对象的关系,在前面说过WP是将访问者的要求转交给ISAPIRuntime对象,此时要求仍然是ISAPI的数据封装包格式,ISAPIRuntime对象解析出这些信息后执行HttpApplication.PreccessRequest函数,而该函数则创建HttpApplication及HttpContext两个对象,以上的叙述可以知道,HttpApplication对象,当有多个访问者访问网站时,就会有HttpApplication对象共存于一个AppDomain中,在目前的.NET Framework 1.x和2.0实现中,单一的AppDomain中最多会有近100个HttpApplication对象同时存在于HttpApplicationFactory对象所管理的Pooling池中,超过此数量而创建HttpApplication对象会在请求完成后立即释放(事实上,这是粗略的说法,因为还要加上HttpApplicationFactory对象初始化期间处理其他访问请求而额外创建的HttpApplication对象)。
理清这些头绪以后,细心的朋友可能会发现另外一个问题,那就是HttpApplication对象为何能够共享Cache,Application还有State对象呢?答案很简单,这两个对象是由HttpRuntime对象管理的,HttpContext、HttpApplication对象只是单纯地做了一个转交而已,一个AppDomain中只会有一个HttpRuntime对象存在。你可能又要问了:HttpModule,HttpApplication对象是共享一组HttpModule对象还是分别拥有独立的一组呢?答案是后者,每个HttpApplication都会创建专属的一组HttpModule对象。那么Session呢?前面提过Session是由SessionState Module管理的,那么这在多个HttpApplication对象情况下又如何呢?这个问题实在不好回答,为了让Session处理更经济而且更加有效率,SessionState Module采取相当复杂的处理,其间调用了CacheInternal对象来存储真正的Session元素,简单地说:多个SessionState Module存取同一个CacheInternal对象,而一个HttpRuntime对象管理着一个 CacheInternal对象,因此,Session不会受到多个HttpApplication对象加载各个独立HttpModule对象所影响。
还有一些东西还没有说完,等在下整理好之后一并发上来!如果有什么错误,请大家不要吝惜自己的击键时间,多多提意见!在下感激不尽!