• ASP.NET 运行时学习


          ASP.NET2.0运行时在我看来一直是一项高深莫测的的知识,每每看到它时总是将其跳过去,因为我觉得其中的奥妙或许不是现阶段的我能够理解的.但在学习控件开发的过程中越来越多的疑问不能顺利地解决,回过头来看正是它卡住了我前行的步伐,是有必要去深入地了解下它了,花了几天的时间整理了下关于asp.net2.0运行时的相关内容,结合他人的想法和自己的一些理解对其做了一些总结,但感觉asp.net2.0运行时还是很复杂,自己的理解或许只是停留在表面上的,由于能力的限制,文中难免有错误的地方,欢迎批评指正.
          一、概念入手,什么是ASP.NET2.0运行时?
          ASP.NET运行时指从asp.net框架接收到客户端请求,到合适的HttpHandle接管求求之间,以及从HttpHandlers生成处理结果后到发送请求处理结果到客户端之间,ASP.NET框架所完成的一系列工作和逻辑.
          从概念上来看似乎看不出什么端倪,也看不出关于asp.net2.0运行时的任何内部机理.所以我们从ASP.NET应用程序的宿主IIS说起,一般我们发布一个asp.net应用程序时,我们都会在IIS中选择一个应用池,一般默认的是DefaultAppPool,当然我们可以建立多个应用池,但建立这么多的应用池有什么用呢?一般来说一个应用池可以也只能设置一个版本的asp.net Framework,还可以设置它的最大内存数量、CPU使用率、回收工作进程的时间间隔等参数,因此设置多个应用池可以对应地发布不同的asp.net应用程序,默认情况下,每个应用程序池由一个w3wp.exe进程来维护(这也是为什么每次我们本地IIS部署并开启一个asp.net网站时会有一个w3wp.exe进程在那运行着).
          IIS中不仅仅可以发布asp.net应用程序还可以发布asp,php应用程序,如何区别这些不同应用程序发过来的请求呢?其实,IIS处理这些不同的请求是通过各种ISAPI的扩展来执行的,IIS根据不同的请求,将请求指定到不同的ISAPI扩展,如果说客户端发送请求的是一个aspx页面,IIS就会将这个请求发给asp.net特有的扩展aspnet_isapi处理,如果我们未安装这个扩展,就会提示"HTTP 错误 404,您正在搜索的页面可能已经删除、更名或暂时不可用 ".至于aspnet_isapi如何处理这个请求,我们可以先从ISAPIRunTime这个类入手.
          二、ISAPIRunTime
           这个类位于System.Web.Hosting命名空间下,是一个密封类,aspnet_isapi对于IIS交给它的请求它第一时间以COM方式调用了IISAPIRunTime对象的ProcessRequest方法,这也是从托管环境至非托管环境的一个交界点.aspnet_isapi.dll存在于非托管环境中,它通过加载CLR创建了一个托管的环境,而这个过程是异步的,aspnet_isapi一调用ISAPIRunTime对象的ProcessRequest方法后就立即返回,只将自己对应的ECB的指针传给它,ISAPIRutime不但可以将最终生成的Response返回给ISAPI,还能通过ECB调用ISAPI获得一些所需的数据。
    public int ProcessRequest(IntPtr ecb, int iWRType)
    {
        
    try
        
    {
              //ecb是aspnet_isapi异步调用ISAPIRunTime对象的ProcessRequest方法后传递的指针,这也是从托管环境进入非托管环境的一个交界点.

            HttpWorkerRequest wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, iWRType);
            
    string appPathTranslated = wr.GetAppPathTranslated();
            
    string appDomainAppPathInternal = HttpRuntime.AppDomainAppPathInternal;
            
    if ((appDomainAppPathInternal == null|| StringUtil.EqualsIgnoreCase
                      (appPathTranslated, appDomainAppPathInternal))
            
    {
                HttpRuntime.ProcessRequestNoDemand(wr);
                
    return 0;
            }

            HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged,
    SR.GetString(
    "Hosting_Phys_Path_Changed"new object[] { appDomainAppPathInternal, appPathTranslated }));
        }

        
    catch (Exception exception)
        
    {
            Misc.ReportUnhandledException(exception, 
    new string[]
              
    { SR.GetString("Failed_to_process_request") });
            
    throw;
        }

        
    return 1;
    }


    这段代码的其余部分可以不用去深入地理解它,但我们需要知道的是从HttpRuntime.ProcessRequestNoDemand(wr)这里,我们就进入了HttpRunTime对象,这也是本篇文章所重点论述的.
        三、HttpRunTime
          HttpRunTime这个类位于System.Web命名空间下,是System.Web组件中最为复杂的几个类之一.这是一个密封类,因此我们不能去继承修改它.这个类在asp.net2.0运行时起着一个主导的作用.从HttpRunTime这个对象的ProcessRequestInternal方法我们来看看它到底做了什么.
    private void ProcessRequestInternal(HttpWorkerRequest wr)
    {
          //创建一个HttpContext实例,每一个请求都会对应一个HttpContext对象,而每一个对象都管理着一个HttpSession对象,这也保证了每次请求均有一个Session对象可以使用.
        HttpContext extraData 
    = new HttpContext(wr, false);
        wr.SetEndOfSendNotification(
    this._asyncEndOfSendCallback, extraData);
        Interlocked.Increment(
    ref this._activeRequestCount);
        HostingEnvironment.IncrementBusyCount();
        
    try
        
    {
            
    try
            
    {
                   //对第一次请求初始化,在这个过程中过调用System.Web.HttpRuntime.FirstRequestInit进行一些初始化工作,比如:将Web.Config配置读到到RuntimeConfig中,从bin目录中装载所有dll文件

                
    this.EnsureFirstRequestInit(extraData);
            }

            
    catch
            
    {
                
    if (!extraData.Request.IsDebuggingRequest)
                
    {
                    
    throw;
                }

            }

            extraData.Response.InitResponseWriter();
       IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(extraData);
             //HttpApplication是在这里创建的,这里引用dudu对这个方法的分析.
              
    通过调用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.asax文件,也就是说没有动态编译生成ASP.global_asax类型,那就直接实例化HttpApplication。如果创建了ASP.global_asax 类型,那就对ASP.global_asa进行实例化。

            
    if (applicationInstance == null)
            
    {
                
    throw new HttpException(SR.GetString("Unable_create_app_object"));
            }

            
    if (EtwTrace.IsTraceEnabled(51))
            
    {
                EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER, extraData.WorkerRequest,
                   applicationInstance.GetType().FullName, 
    "Start");
            }

            
    if (applicationInstance is IHttpAsyncHandler)
            
    {
                IHttpAsyncHandler handler2 
    = (IHttpAsyncHandler) applicationInstance;
                extraData.AsyncAppHandler 
    = handler2;
                handler2.BeginProcessRequest(extraData, 
    this._handlerCompletionCallback, extraData);
            }

            
    else
            
    {
                applicationInstance.ProcessRequest(extraData);
                
    this.FinishRequest(extraData.WorkerRequest, extraData, null);
            }

        }

        
    catch (Exception exception)
        
    {
            extraData.Response.InitResponseWriter();
            
    this.FinishRequest(wr, extraData, exception);
        }

    }
    从代码上看asp.net运行时总显得很枯燥,笔者通过自己的理解画了一幅流程图,撇开了大部分代码

    HttpApplicationFactory.GetApplicationInstance方法开始

    获取global.asax文件的完整路径进行编译,编译的顺序是App_GlobalResourcesèApp_WebReferencesèApp_CodeèGlobal.asax,执行Application_Start事件

    根据webconfig中的设置创建相应的HttpModules

    对请求的路径进行检查

    如果有设置UrlMapping就进行UrlReWrite

    进行相应的处理函数(BeginRequest,AuthenticateRequest)

    获取处理请求的HttpHandler,包括ASP.NET页面的动态编译等

    <!--[if !vml]--><!--[endif]-->


    asp.net2.0运行时牵涉到的内容很多,本人对.net运行时的理解或许只是停留在表面上的,如果朋友对.net运行时感兴趣的话,可以学习如下几位大牛的文章,本文的很大一部分也是参考如下文章,希望本篇文章能对你学习asp.net2.0运行时带来一点帮助.
     dudu的asp.net运行时简要分析
    EagleFish的从.net类库看.net运行时
    Artech的ASP.NET Process Model之二:ASP.NET Http RunTime Pipeline-PartI
    Artech的ASP.NET Process Model之二:ASP.NET Http RunTime Pipeline-PartII
    文野的一点一点学ASP.NET之基础概念——HTTP运行期与页面执行模型
    头中中,不大的ASP.NET的(HttpModule,HttpHandler)
  • 相关阅读:
    关于异步IO与同步IO的写操作区别
    慢慢开始记录一些技术心得吧
    写了placement new就要写placement delete
    关于针对class自定义new操作符失败的函数处理
    operator->和operator->*
    关于继承中的拷贝构造函数
    关于g++编译模板类的问题
    关于互斥锁,条件变量的内核源码解析
    关于sigwait
    观察者设计模式
  • 原文地址:https://www.cnblogs.com/answercard/p/1449214.html
Copyright © 2020-2023  润新知