ASP.NET运行原理

一个ASP.NET的应用程序是开始于IIS的.

当你请求一个包含ASP.NET应用的网址时,IIS接受到请求(IIS是WEB服务守候进程),IIS收到请求后,会根据请求者请求的主机头或者IP或者端口号来找到对应的站点.


当 找到站点后,如果你请求的资源是以ASPX为结尾的WEBFORM,时,IIS会将控制权交给一个ISAPI扩展.,名叫 AspNet_ISAIP.DLL.这时,控制权由IIS交到ASPNET的ISAPI扩展上.,需要说明的是,ISAPI扩展的级别低于IIS,但高于 用户站点,它独立于站点之外


ISAPI收到处理请求后,会启动一个ASP.NET工作进程.然后将请求者的请求信息转交给 ASP.NET工作进程(名为ASPNET_WP.EXE).接下来,控制权由ASPNET_WP掌握.ASPNET_WP首先解出请求者的信息,如果请 求者请求的ASP.NET应用程序(站点或虚拟目录,通俗一点)尚未拥有APPDOMAIN,ASPNET_WP就会建立一个APPDOMAIN,并且将 被请求的ASP.NET应用所需的Assembly(就是那些DLL,例如System.Web.DLL等)载入到APPDOMAIN中


以上的步骤可以看到一个结论和规律:控制权是以流水式在各个请求处理者间传递,并且,前一个处理请求者必须负责传递后一个处理请求者所需的信息.而且要负责装载或初始化后一个处理者,这很像我们生活中的接力赛.


问 题是,可能有许多人会问:干嘛要如此繁琐呢?直接由IIS把请求转交给ASPNET_WP如何呢?不是不可以,而是如此一来,这个处理过程的可扩展性就变 低了.ASPNET ISAPI是IIS和ASPNET_WP之间的桥梁.虽然看起来它仅仅负责转交请求等工作.可是这样一来,就大大扩展延展性.


另 外一个疑问是关于APPDOMAIN的,包括我,对于APPDOMAIN一开始的理解就曾陷入误区,APPDOMAIN这东东微软讲的也比较含糊,有人说 跟进程一样,但我一开始理解成了IIS里的应用程序池,所以,走了不少弯路,实际上,APPDOMAIN既不是进程,也不是IIS里的应用程序池概 念..NET下的所有应用程序都运行于APPDOMAIN之中(我自己的理解),每一个APPDOMAIN是一个执行的容器,每执行一个应用程序或者 ASP.NET应用,.NET执行环境就会建立一个APPDOMAIN,然后把应用程序需要的一些DLL载入.APPDOMAIN的功能很像进程,但绝不 是进程.你可以这样理解,APPDOMAIN就是ASP.NET应用程序的执行环境吧.


AspNet_WP不光负责建立 APPDOMAIN(当然,如果已经存在的话,就直接使用这个DOMAIN了),另外,它在APPDOMAIN建立后,还会将请求转发至对应的 APPDOMAIN中的ISAPIRuntime对象。(Isapiruntime对象是APPDOMAIN的一部分)。ISAPIRUNTIME专门负 责解出请求的必要信息。它将信息和请求转交给HttpRuntime。在这里,需要说明的是IsapiRuntime是一个类,它的全称是 System.Web.Hosting.ISAPIRuntime,而HttpRuntime也是一个类,它的全称是 System.Web.HttpRuntime。因此,可以说,这两个对象是APPDOMAIN运行环境的一部分,在ASPNET_WP建立 APPDOMAIN的同时,也会作为运行环境来建立这两个对象.


由于接二连三的讲述了几个对象,所以,当我第一遍看这本书特别是 看到这部分时,觉得特别晕,因为第一对.NET FRAMEWORK的类库不甚了解,第二,对ASP.NET的运行原理初次接触.摸不着头脑,总想把这些对象名称与某个DLL或者某个实际上的文件来对 应.其实不然,不管是ISAPIRuntime也好,还是HttpRuntime,它们在APPDOMAIN建立时,作为APPDOMAIN的一部分被实 例化.所以它们代表的是内存中的一个类的实例,也就是对象.并且,这上面的一部分运作原理,似乎跟ASP.NET应用程序没有直接联系.似乎不入正题,很 容易让初看者不知所云.实际上,可以说,由IIS到ISAPI是完成了请求的第一个部,也就是接纳客户请求.由ISAPI到APPDOMAIN,是第二部 分,也就是初始化部分,旨在建立处理请求的大环境,为下面处理请求和运行ASP.NET应用程序作好准备.


接下来,当 APPDOMAIN初始化完成后,接下来就需要建立会话了吧,因此,请求由HttpRuntime来接受,HttpRunTime主要的工作便是为每一个 提出请求的客户建立一个HttpContext对象.这个东东又管理着HttpSession对象.每一个访问者有各自的HttpContext对象和 HttpSession对象,这些对象,你可以在.NET FRAMEWORK库中找到对应的类名,像System.Web.HttpContext,System.Web.HttpSessionState等.


可以看出,请求的处理过程非常类似于.NET中事件模型的处理过程.若干个处理模块被串接到一个事件上.在ASP.NET运行原理里,也是,若干个模块依次轮流处理一个请求,像流水线操作一样.


另外,作为组件开发者,还要明确一个HttpRuntime,HttpContext,HttpSession这些对象的层次关系和调用创建关系.细节部分无需了解,只要知道谁创建了谁,谁被谁调用即可


HttpRuntime负责创建HttpContext和HttpSession,httpContext负责管理httpSession



到HttpRuntime创建完httpContext为止,实际上,你的应用程序仍然没有运行,或者说,请求者的请求实际上并未真正的被处理,前面的工作都是些准备性或者辅助性的工作.


HttpRuntime除了创建上面的对象外,还要创建HttpApplication.至于创建Application对象的过程,是比较复杂的.你可以把其作为一个分支流程涉略一下




接下来,HttpApplication调用ProcessRequest方法来处理用户请求,此方法会调用对应的HttpHandler来处理用户请求,HttpHandler根据用户请求的文件的扩展名处理请求,并把请求的结果,也就是HTML发送到客户浏览器


另 外,过程的复杂性远远超出了上面的描述,基本上,黄先生这本书的第三章第一节用了十几页文本在描述ASP.NET运行过程及原理,以及处理请求时用的一些 手法,但总体上的过程如上面的描述那样,只不过,我没有将建立各种对象时的细节剥离出来展示给大家.黄先生原著上的这节内容实际上非常详细.但为何大家看 起来均言吃力呢?一方面是因为原理部分一向比较麻烦,另外一方面,是因为黄先生在讲述时,没有先向大家概要的描述过程和纲领,然后再描述细节,再是直接把 细节和纲领融合在一起.这样,如果看的时候,没有去将这节的各个小标题和内容串联起来并先总结出纲领来的话.看完后,就会头晕.实际上,整个讲述就是在描 述ASP.NET处理请求的过程.如果隐藏所有技术性的细节,而只讲流程的话,大家可能很快理解.然后再将流程中的每一部分的技术细节展现出来,我想,容 易理解的多.这好比讲故事,先将故事梗概说一下比较好吧.


当然,我不是说黄先生写的不好,实际上,这一节写的很透彻,看懂了,就很受用.流程是很重要的,它的重要性在于你知道了在何时发生何事,就可以在指定的时间点做一些处理.这一点,在黄先生本书以后的章节中讲述ASP.NET PAGE对象执行流程中更显重要.


下面的图对整个ASP.NET应用运行过程中的各个对象的职能以及流程做了图解.当然,图解抛弃了技术性的细节,例如,像HttpApplication如何建立等






asp.net运行原理

System.Web.HttpRuntime

System.Web.HttpApplicationFactory

System.Web.HttpApplication

System.Web.Compilation.BuildManager

System.Web.Compilation.ApplicationBuildProvider

System.Web.Compilation.BuildProvidersCompiler

System.Web.UI.PageHandlerFactory

  请求处理简要流程图:




  阅读建议:

用Reflector工具边查看ASP.NET 2.0的源代码边阅读。


  分析:

      当 我们通过浏览器向ASP.NET 2.0网站的一个asp.net页面发起请求时,在服务器端首先是IIS收到请求,IIS一看是asp.net页面,心里很开心,因为这个请求不用它处 理,交给ASP.NET ISAPI就行了。ASP.NET ISAPI的工作也比较轻松,他的主要任务就是安排aspnet_wp.exe处理请求,并监视aspnet_wp.exe进程的执行情况,如果 aspnet_wp.exe进程太累了,不能出色地完成任务,ASP.NET ISAPI就要让他下岗,换一个新的aspnet_wp.exe来处理工作。

aspnet_wp.exe的主要任务是将请求交给一 系列称为的 HTTP 管道的托管对象。如果把ASP.NET ISAPI比做销售经理,那aspnet_wp.exe就是生产经理,而HTTP 管道就是生产的流水线。负责流水线的小组就是HttpRuntime,生产经理aspnet_wp.exe会将订单(HTTP请求)交给 HttpRuntime小组的工作人员ProcessRequest(HttpWorkerRequest wr),HttpRuntime根据内部的分工,最终由ProcessRequestInternal(HttpWorkerRequest wr)在流水线上进行生产,所以ProcessRequestInternal(HttpWorkerRequest wr)是我们分析的重点。

ProcessRequestInternal的主要工作是:

1. 创建HttpContext实例。

2. 对第一次请求进行初始化(EnsureFirstRequestInit)。

a) 在EnsureFirstRequestInit中通过调用System.Web.HttpRuntime.FirstRequestInit进 行一些初始化工作,比如:将Web.Config配置读到到RuntimeConfig中,从bin目录中装载所有dll文件。

3. 创建HttpWriter实例。

4. 通过调用HttpApplicationFactory.GetApplicationInstance创建HttpApplication实例。

在HttpApplicationFactory.GetApplicationInstance中有三个关键方法:

HttpApplicationFactory._theApplicationFactory.EnsureInited();

HttpApplicationFactory._theApplicationFactory.EnsureAppStartCalled(context);

HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(context);

  下面我们对这三个方法逐个进行分析:

1) HttpApplicationFactory._theApplicationFactory.EnsureInited();

该方法检查HttpApplicationFactory是否被初始化,如果没有,就通过HttpApplicationFactory.Init()进行初始化。

在Init()中,先获取global.asax文件的完整路径,然后调用CompileApplication()对global.asax进行编译。

那编译是如何进行的呢?

编 译的工作由BuildManager完成的。BuildManager先得到GlobalAsaxType(也就是 HttpApplication),然后调用BuildManager.GetGlobalAsaxBuildResult()=》 GetGlobalAsaxBuildResultInternal()=》EnsureTopLevelFilesCompiled()进行编译。

在EnsureTopLevelFilesCompiled中,先进行CompilationStage.TopLevelFiles编译,对下面三个目录中的文件进行编译:

a. CompileResourcesDirectory();

编译App_GlobalResources目录。

b. CompileWebRefDirectory();

编译App_WebReferences目录。

c. CompileCodeDirectories();

编译App_Code目录。

   接着进行CompilationStage.GlobalAsax 编译,对global.asax进行编译,方法调用情况:CompileGlobalAsax()=》 ApplicationBuildProvider.GetGlobalAsaxBuildResult(BuildManager.IsPrecompiledApp)。

在GetGlobalAsaxBuildResult中具体的编译是由ApplicationBuildProvider与BuildProvidersCompiler共同完成的。

BuildProvidersCompiler.PerformBuild();进行编译工作。

ApplicationBuildProvider.GetBuildResult得到编译的结果。

编译成功后,会在C:"WINDOWS"Microsoft.NET"Framework"v2.0.50727"Temporary ASP.NET Files"相应的目录中生成类似App_global.asax.mlgx7n2v.dll的dll文件。

编译生成的类名为ASP.global_asax,继承自HttpApplication。

注:如果Web目录中没有Global.asax文件,就不会编译生成App_global.asax.mlgx7n2v.dll这样的文件。

  2) HttpApplicationFactory._theApplicationFactory.EnsureAppStartCalled(context);

创 建特定的HttpApplication实例,触发ApplicationOnStart事件,执行ASP.global_asax中的 Application_Start(object sender, EventArgs e)方法。这里创建的HttpApplication实例在处理完事件后,就被回收。

  3) HttpApplicationFactory._theApplicationFactory.GetNormalApplicationInstance(context);

该方法创建HttpApplication实例并进行初始化(调用System.Web.HttpApplication. InitInternal()方法)。

创 建HttpApplication实例是根据实际的_theApplicationType进行创建。如果Web目录中没有global.asa文件,也 就是说没有动态编译生成ASP.global_asax类型,那就直接实例化HttpApplication。如果创建了ASP.global_asax 类型,那就对ASP.global_asa进行实例化。

创建HttpApplication实例之后就是调用实例的InitInternal方法。

InitInternal方法也是我们重点分析的方法,该方法的主要功能如下:

1. InitModules():根据Web.Config的设置,创建相应的HttpModules。

2. HookupEventHandlersForAppplicationAndModules:根据发生的事件,调用HttpApplication实例中相应的事件处理函数。

3. 创建很多实现IExecutionStep接口的类的实例并添加到当前HttpApplication实例的_execSteps中,等待回调时 执行。从这里我们可以看到HttpApplication是以异步的方式处理请求,对请求的很多处理工作都放入了_execStep等待回调时执行。

_execStep中主要的处理工作如下:

1) 对请求的路径进行安全检查,禁止非法路径访问(ValidatePathExecutionStep)。

2) 如果设置了UrlMappings, 进行RewritePath(UrlMappingsExecutionStep)。

3) 执行事件处理函数,比如:BeginRequest、AuthenticateRequest等等。

4) 获取处理当前请求的HttpHandler,ASP.NET页面的运行时编译也是在这里进行的。(MapHandlerExecutionStep)

该处理是通过调用System.Web.HttpApplication. MapHttpHandler方法。

在 MapHttpHandler中,首先根据访问的地址从web.config获取相应的实现IHttpHandlerFactory的类型。对于 asp.net页面,默认是PageHanlderFactory。然后创建PageHanlderFactory实例,调用 GetHandlerHelper,在GetHandlerHelper中调用 BuildManager.CreateInstanceFromVirtualPath编译并创建当前请求的ASP.NET页面的实例(如果已经编译 过,直接从缓存中加载)。

CreateInstanceFromVirtualPath经过几次方法调用,将编译任务给了 BuildManager. CompileWebFile()。CompileWebFile从web.config得到相应的BuildProvider,对于.aspx文件,相 应的BuildProvider是PageBuildProvider。PageBuildProvider是如何进行页面编译的,这里就不再就进一步分 析了,如果你感兴趣,可以进一步研究ASP.NET 2.0的源代码。

5) 调用相应HttpHandler的.ProcessRequest方法处理请求(如果是异步方式,调用BeginProcessReques)。(CallHandlerExecutionStep)

6) 将响应内容写入Filter。(CallFilterExecutionStep)

  5. 调用HttpApplication实例的BeginProcessRequest异步处理请求。

     上面所讲的_execSteps中所发生的许多事情,都是在HttpRuntime调用HttpApplication BeginProcessRequest之后,在BeginProcessRequest中调用ResumeSteps后执行的。

  ASP.NET 2.0运行时是ASP.NET 2.0中非常复杂、难以理解也是很重要的部分,对ASP.NET 2.0运行时源代码的研究有处于我们加深对ASP.NET 2.0原理的理解,会给我们开发ASP.NET 2.0应用程序带来不少帮助

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值