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运行时总显得很枯燥,笔者通过自己的理解画了一幅流程图,撇开了大部分代码

ASP.NET 运行时学习
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)

你可能感兴趣的:(asp.net)