• 休闲生活
  • 教育教学
  • 经济贸易
  • 政法军事
  • 人文社科
  • 农林牧渔
  • 信息科技
  • 建筑房产
  • 环境安全
  • 当前位置: 达达文档网 > 达达文库 > 农林牧渔 > 正文

    ASP.NET运行时模型的源码解析

    时间:2020-09-19 07:55:24 来源:达达文档网 本文已影响 达达文档网手机站

    摘要:ASP.NET是微软公司推出的基于DotNet平台的网络开发技术,对B/S模式应用的开发提供了强大的支持,该文从源代码级别进入ASP.NET底层,分析ASP.NET运行时模型,揭开ASP.NET应用程序运行的幕后细节。

    关键词:ASP.NET;DotNet平台;网络开发;B/S模式应用;运行时模型

    中图分类号:TP311文献标识码:A文章编号:1009-3044(2008)33-1415-04

    SourceCode Analyzing for ASP.NET Runtime Model

    ZHANG Jia-qing

    (Fujian Institude of Economics Management, Fuzhou 350002, China)

    Abstract: Based on DotNet platform, Microsoft Proposed the network developing technology called ASP.NET which provides strong support to the development of B/S pattern application programs. This paper enters into the bottom of ASP.NET from the level of source code so as to analyze the runtime model of ASP.NET, so that running details behind the curtain of ASP.NET application programs are uncovered.

    Key words: ASP.NET; DotNet platform; network developing; B/S pattern application; runtime model

    1 引言

    ASP.NET运行时模型指的是ASP.NET 框架在接收到客户端请求,到获取合适的HttpHandler接管请求之间,以及从HttpHandlers生成处理结果到发送处理结果给客户端之间,ASP.NET 框架所完成的一系列工作和处理逻辑[1]。本文将通过源代码级别的分析,深入讨论ASP.NET 2.0运行时模型,解析ASP.NET应用程序运行时模型的工作原理。

    2 ISAPI接口标准与IIS服务器的可扩展性

    IIS服务器是部署ASP.NET应用的标准Web服务器,由于IIS服务器在设计时引入了开放的ISAPI接口标准,具备极高的可扩展性,在核心组件不变的情况下可灵活支持不同类型不同版本的应用,例如IIS 5.1版能够同时支持ASP.NET 1.0、1.1、2.0,甚至可以支持部署基于DotNetFramework 3.5的ASP.NET应用程序。ISAPI的全称是:Internet Server Application Programming Interface,即Internet服务器应用编程接口,它为开发人员提供了强大的可编程能力,只要按照标准接口开发不同类型Web应用程序的ISAPI扩展程序,就能实现对IIS功能上的扩展,从而使IIS可以处理不同类型的客户端请求,例如让IIS处理Perl或PHP应用程序[2]。

    IIS管理器提供了应用程序配置功能,可以对不同的客户端请求配置不同的ISAPI扩展程序。ISAPI扩展程序通常以DLL的形式存在,可以被IIS加载并调用。在ASP.NET2.0下,对应于.aspx的ASP.NET应用程序,其ISAPI扩展程序默认情况下位于C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\目录下,以aspnet_isapi.dll(下文简称aspnet_isapi)文件的形式存在[3]。

    有了ISAPI扩展,IIS服务器就可以根据客户端请求的资源扩展名,来决定应由哪个ISAPI扩展来处理客户端请求,然后就可以将请求转发给合适的ISAPI扩展。例如当访问资源的扩展名为.aspx、.ascx、.ashx 和 .asmx时,IIS服务器会自动将请求转发给缺省情况下位于C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\目录下aspnet_isapi.dll进行处理[4],这个动态库就是一个ISAPI扩展程序,负责处理ASP.NET应用程序的请求。

    3 ASP.NET的后台辅助进程aspnet_wp.exe

    实际上客户发起的请求最终要由aspnet_isapi传递给它背后的aspnet_wp.exe去处理,.Net平台将其称为ASP.NET Worker Process(下文简称WP),该文件位于.NET framework安装目录下,与aspnet_isapi.dll所在位置相同。

    当aspnet_isapi接收到IIS转发的ASP.NET请求后,会将请求放入队列,并根据实际情况分配请求处理任务给WP进程,一旦请求被转送给WP进程,WP进程便会通知aspnet_isapi,请求正在被处理。这个通知的过程是通过同步I/0完成的,这么实现目的是为了保证处理过程的完整性,因为只有当请求在aspnet_isapi内部被标记为“executing”后,WP才会真正开始处理该请求。此后请求便在WP的上下文环境中执行,当执行结束后,处理结果会通过一个异步的开放管道回送给aspnet_isapi,这时请求的状态会被更新为“Done”,接着请求就会从队列中清除。如果WP进程崩溃,所有正在处理中的请求,都将维持“executing”状态一段时间,等到aspnet_isapi检测到WP进程死掉后,会自动丢弃所有的请求,并释放已经分配的资源[5]。其过程如图1所示。

    WP会分析每一个请求的信息,解析出其中的虚拟目录信息,并检查该虚拟目录对应的AppDomain是否已经存在,如果不存在则创建一个新的AppDomain,然后使用它,否则直接重用已经建立的AppDomain对象。这里的AppDomain指的是.NET中引入的应用程序域的概念,它可以理解为一个进程或一个边界,或一个容器,它是应用程序的执行环境,.NET下所有的应用程序都运行在AppDomain中,每一个ASP.NET应用程序(IIS中的站点或者虚拟目录)都会有一个AppDomain与之对应[6],它保存了Applcation对象、Cache等全局变量。

    4 请求在ASP.NET运行时中的处理,步骤一:ISAPIRuntime->HttpRuntime

    WP接收到aspnet_isapi转发的请求后,就将请求转送给指定虚拟目录对应的AppDomain中的ISAPIRuntime对象,由它完成对aspnet_isapi封装后的请求包的解析工作,笔者使用Lutz Roeder"s .NET Reflector工具查看System.Web.Dll,通过分析ISAPIRuntime的源码发现,客户的请求首先会被该类的ProcessRequest方法处理,该方法的部分代码如下:

    ISAPIWorkerRequest wr = null;

    try{

    bool useOOP = iWRType == 1;

    wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP);

    wr.Initialize();

    ……

    if (……){

    HttpRuntime.ProcessRequestNoDemand(wr);

    return 0;

    }

    ……

    }

    可以看出在ProcessRequest方法中,主要通过调用一些非托管代码生成HttpWorkerRequest对象,在上述代码中,对应于wr对象(ISAPIWorkerRequest类型继承自HttpWorkerRequest类),该对象包含当前请求的所有信息,然后ISAPIRuntime调用HttpRuntime的ProcessRequestNoDemand方法传递创建好的HttpWorkerRequest对象,这个方法的代码如下:

    internal static void ProcessRequestNoDemand(HttpWorkerRequest wr){

    RequestQueue queue = _theRuntime._requestQueue;

    if (queue != null){

    wr = queue.GetRequestToExecute(wr);

    }

    if (wr != null){

    CalculateWaitTimeAndUpdatePerfCounter(wr);

    wr.ResetStartTime();

    ProcessRequestNow(wr);

    }

    }

    该方法先从请求队列中取出一个请求,然后更新请求的引用计数器等信息,接着就让ProcessRequestNow方法处理请求,这个方法的代码如下:

    internal static void ProcessRequestNow(HttpWorkerRequest wr){

    _theRuntime.ProcessRequestInternal(wr);

    }

    追踪分析源码可以发现_theRuntime对象是在HttpRuntime内部定义的,其类型就是HttpRuntime自身,它在HttpRuntime的静态构造函数中被初始化。

    5 请求在ASP.NET运行时中的处理,步骤二:HttpRuntime.ProcessRequestInternal()

    接下来我们重点分析HttpRuntime类ProcessRequestInternal方法的运作细节!使用Reflector工具追踪至ProcessRequestInternal方法的内部:

    HttpContext context;

    context = new HttpContext(wr, false);

    ……

    this.EnsureFirstRequestInit(context);

    context.Response.InitResponseWriter();

    ……

    IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context);

    ……

    IHttpAsyncHandler handler2 = (IHttpAsyncHandler)applicationInstance;

    context.AsyncAppHandler = handler2;

    handler2.BeginProcessRequest(context,this._handlerCompletionCallback, context);

    ……

    通过分析该方法的源码,发现它主要完成如下处理:

    1) 根据HttpWorkerRequest对象初始化HttpContext对象,它包含了request、response等属性,在编程中经常会用到这些重要的属性来完成特定的任务。

    2) 调用EnsureFirstRequestInit方法完成第一次请求的初始化工作,该方法锁定全局变量_beforeFirstRequest,然后调用FirstRequestInit(context)完成应用程序配置文件的加载、初始化请求队列、装载Bin目录下的所有程序集等工作,然后更新_beforeFirstRequest为false。

    3) 执行InitResponseWriter创建HttpWriter对象,用于写入处理结果返回信息。

    4) 调用HttpApplicationFactory类的GetApplicationInstance方法来生成IHttpHandler(这里生成的是一个默认的HttpApplication对象,HttpApplication实现了IHttpHandler接口)。

    5) 调用HttpApplication对象(它同时实现了IHttpAsyncHandler接口)的BeginProcessRequest方法执行客户请求。

    需要注意的是,笔者在跟踪至HttpApplicationFactory类的GetApplicationInstance方法内部时发现它最终通过调用该类的GetNormalApplicationInstance方法获取HttpApplication实例:

    return _theApplicationFactory.GetNormalApplicationInstance(context);

    再深入一步,进入GetNormalApplicationInstance方法内部,我们终于看到HttpApplication对象是如何被创建和初始化的:

    HttpApplication application = null;

    ……

    application = (HttpApplication)HttpRuntime.CreateNonPublicInstance(this._theApplicationType);

    using (new ApplicationImpersonationContext()){

    application.InitInternal(context, this._state, this._eventHandlerMethods);

    }

    我们发现HttpApplication类提供了一个名为InitInternal的方法,调用方通过它来完成HttpApplication实例的初始化工作,在这个方法的内部,有如下代码:

    ……

    this.InitModules();

    ……

    this.HookupEventHandlersForApplicationAndModules(handlers);

    ……

    this._stepManager = new ApplicationStepManager(this);

    this._stepManager.BuildSteps(this._resumeStepsWaitCallback);

    ……

    可以看到在HttpApplication对象初始化时,首先会自动调用自身的InitModules方法来加载在web.config文件中配置的所有HttpModule模块。接着HookupEventHandlersForApplicationAndModules方法被调用,这个方法完成global.asax文件中配置的HttpApplication或HttpModule事件的绑定。最后ApplicationStepManager对象的BuildSteps方法被调用,完成HttpApplication事件的绑定,这个方法很重要,它将创建各种HttpApplication.IExecutionStep对象并保存到一个数组列表中,以便在BeginProcessRequest方法内部调用ResumeSteps方法依次执行这些对象的Execute()方法,完成各种处理。

    图2是以上分析的一个总结。

    图1 请求传递和处理过程图

    图2请求在ASP.NET运行时内部处理过程图

    在取得HttpApplication对象实例之后,HttpRuntime对象开始调用它的BeginProcessRequest方法(实现IHttpAsyncHandler接口中定义的方法)处理请求:

    IAsyncResult IHttpAsyncHandler.BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData){

    ……

    this._stepManager.InitRequest();

    this._context.Root();

    HttpAsyncResult result = new HttpAsyncResult(cb, extraData);

    this.AsyncResult = result;

    ……

    this.ResumeSteps(null);

    return result;

    }

    该方法首先调用ApplicationStepManager对象的InitRequest方法完成一些初始化工作,例如将记录当前执行步骤的变量清0、置请求处理完成标志为false等。然后根据上下文创建HttpAsyncResult对象记录执行结果,最后ResumeSteps方法被调用!这个方法会依次取出在数组列表中的HttpApplication.IExecutionStep对象,传递给HttpApplication的ExecuteStep方法,由它调用执行IExecutionStep对象的Execute()方法。当执行到MapHandlerExecutionStep时,会执行如下代码获取最终执行请求的HttpHandler:

    context.Handler = this._application.MapHttpHandler(context, request.RequestType, request.FilePathObject, request.PhysicalPathInternal, false);

    HttpApplication对象的MapHttpHandler方法将根据配置文件结合请求类型和URL,以调用相应的IHttpHandlerFactory来获取HttpHandler对象,例如与.aspx页面对应的Page类就是一种HttpHandler。此后请求处理的执行权被转交至对应的HttpHandler对象上。至此,本文通过源代码分析技术,详细解析了ASP.NET应用程序在底层的运作细节,限于篇幅,无法继续深入讨论ASP.NET页面的编译及执行过程。

    6 结语

    ASP.NET运行时是整个ASP.NET技术框架中最为复杂、最难以理解,但却十分重要的部分,对ASP.NET 2.0运行时源代码的研究有助于我们加深对ASP.NET 技术的理解,将为我们开发ASP.NET 2.0应用程序带来很多帮助。

    参考文献:

    [1] 谭振林.道不远人—深入解析ASP.NET 2.0 控件开发[M].北京:电子工业出版社,2007:15-18.

    [2] Dr Khosravi S.Professional IIS 7 and ASP.NET Integrated Programming[M].北京:JOHN WILEY & SONS INC,2005:8-9.

    [3] 奚江华.圣殿祭司的ASP.NET 2.0开发详解——使用C#[M].2版.北京:电子工业出版社,2008:49-51.

    [4] Mitchell S,Walther S.ASP.NET权威指南[M].汤涛,译.北京:中国电力出版社,2003:551-553.

    [5] Esposito D.The ASP.NET HTTP Runtime[EB/OL].[2003-07-10].http://msdn.microsoft.com/en-us/library/aa479328.aspx.

    [6] Evjen B,Hanselman S,Muhammad F.ASP.NET 2.0 高级编程[M].李敏波,译.4版.北京:清华大学出版社,2006:371-373.

    相关热词搜索: 源码 模型 解析 运行 ASP

    • 生活居家
    • 情感人生
    • 社会财经
    • 文化
    • 职场
    • 教育
    • 电脑上网