zoukankan      html  css  js  c++  java
  • 一起谈.NET技术,ASP.NET Process Model之二:ASP.NET Http Runtime Pipeline[上篇] 狼人:

      ASP.NET Process Model索引

      相信大家都使用过ASP.NET进行过基于Web的应用开发,ASP.NET是什么?如果站在一个相对High Level的角度,我们可以这样来定义ASP.NET:ASP.NET是一个基于Web的开发平台,提供构建企业级应用所需的Service、Programming Model和Software的Infrastructure。如果我们以一个Low Level的角度来看,它本质上就是一个消息处理器:他接受IIS(确切地说应该是ASP.NET ISAPI)Forward的Http Request (我们可以看成是一个Request Message),经过一系列的处理,最终产生一个用户希望的Response(这也是一个Message,对于.aspx Page来说是一个Html document,对于一个Web Service来说是一个Soap)。所以本篇文章的主要目的在于站在一个相对Low Level的角度介绍ASP.NET的整个Http Request Processing Model。我们访问一个基于ASP.NET的资源,IIS是第一道屏障,在第一个部分我分别就IIS 5.x和IIS 6的差异介绍了IIS对Http Request的处理,今天我们来继续后面的故事。

      一、从Unmanaged Environment到Managed Environment

      上一部分我们说到IIS收到一个基于ASP.NET资源文件的访问,它会把Http Request交给一个ASP.NET ISAPI Extension处理。ASP.NET ISAPI 会加载CLR,从而创建一个托管的环境。ASP.NET ISAPI Extension定义在一个名为aspnet_isapi.dll中,aspnet_isapi.dll是一个纯Native的、高效的Dll,也就是说,虽然ASP.NET ISAPI通过加载CLR创建一个托管的环境,但是ASP.NET ISAPI本省却运行在一个Unmanaged的环境中。而我们的ASP.NET Application确是完全的Managed code,运行在一个Managed的环境中。要了解ASP.NET Http Runtime Pipeline这个纯托管的Runtime,我们必须先了解从Unmanaged Environment到Managed Environment的这道桥梁。

      上图简单表述了在IIS 6环境下,从非托管环境到托管环境的过程。从图中我们可以看到,ASP.NET ISAPI运行在一个非托管环境之中。ASP.NET ISAPI经过系列COM级别的class调用(由于这些被调用的Class都是一个个undocumented class,所以要真正说清楚调用流程中每个具体的细节是一件很难的事情,而且也确实没有很大的必要去挖掘它,因为具体的实现可能会经常变动,如果对此具有好奇心的朋友可以通过一些Tool,比如Reflector去仔细研究一下),最终的调用降临到一个托管的、继承自System.Web.Hosting.ISAPIRuntime类的对象上。ISAPIRuntime 是一个特殊的class,他实现了Interface System.Web.Hosting.IISAPIRuntime。下面是该Interface的定义。通过定义我们可以看到,这是一个基于COM的Interface,也就是说Caller可以通过COM的方式调用实现该Interface的Class的对象。在这里,这个最初的Caller就是ASP.NET ISAPI。从这里我们可以总结出:ASP.NET ISAPI通过调用System.Web.Hosting.ISAPIRuntime Instance的ProcessRequest方法,进而从非托管的环境进入了托管的环境。

    [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("08a2c56f-7c16-41c1-a8be-432917a1a2d1")]
    public interface IISAPIRuntime
    {
    void StartProcessing();
    void StopProcessing();
    [
    return: MarshalAs(UnmanagedType.I4)]
    int ProcessRequest([In] IntPtr ecb, [In, MarshalAs(UnmanagedType.I4)] int useProcessModel);
    void DoGCCollect();
    }

      ISAPI ECB (Execution Control Block) & ISAPIWorkerRequest

      通过System.Web.Hosting.IISAPIRuntime Interface中的ProcessRequest方法的Siganature,我们可以看出该方法包含两个参数,其中一个是名为ecb的Unmanaged Pointer,另一个是useProcessModel。ECB全称是Execution Control Block,在整个Http Request Processing过程中起着非常重要的作用,我们现在来简单介绍一个ECB。

      ISAPI顾名思义,就是实现了一些基于Internet Server的API。Aspnet_isapi.dll实现了这些API,对于IIS来说,它可以调用这些API进入托管的环境实现对ISAPIRuntime的调用,对于ISAPIRuntime来说,它需要调用ASP.NET ISAPI实现一些必要的功能,比如获得Server Variable的数据,获得通过Post Mehod传回Server的数据;以及最终将Response的内容返回给ASP.NET ISAPI,并通过ASP.NET ISAPI返回到Client。一般地ISAPIRuntime不能直接调用ASP.NET ISAPI,而是通过一个对象指针实现对其的调用,这个对象就是ECB,ECB实现了对ISAPI的访问。

      还有一点特别需要强调的是,ISAPI对ISAPIRutime的调用是异步的,也就是说ISAPI调用ISAPIRutime之后立即返回。这主要是出于Performance和Responsibility考虑的,因为ASP.NET Application天生就是一个多线程的应用,为了具有更好的响应能力,异步操作是最有效的解决方式。但是这里就会有一个问题,我们知道我们对ASP.NET 资源的调用本质上是一个Request/Response的Message Exchange Pattern,异步调用往往意味着ISAPI将Request传递给ISAPIRuntime,将不能得到ISAPIRuntime最终生成的Response,这显然是不能接受的。而ECB解决了这个问题,ISAPI在调用ISAPIRutime的ProcessRequest方法时会将自己对应的ECB的指针传给它,ISAPIRutime不但可以将最终生成的Response返回给ISAPI,还能通过ECB调用ISAPI获得一些所需的数据。

      明白ECB是怎么回事之后,我们通过Reflector简单了解一下ISAPIRutime的ProcessRequest的实现: 


    ProcessRequest
    public int ProcessRequest(IntPtr ecb, int iWRType)
    {
    IntPtr zero
    = IntPtr.Zero;
    if (iWRType == 2)
    {
    zero
    = ecb;
    ecb
    = UnsafeNativeMethods.GetEcb(zero);
    }
    ISAPIWorkerRequest wr
    = null;
    try
    {
    bool useOOP = iWRType == 1;
    wr
    = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP);
    wr.Initialize();
    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 }));
    return 1;
    }
    catch (Exception exception)
    {
    try
    {
    WebBaseEvent.RaiseRuntimeError(exception,
    this);
    }
    catch
    {
    }
    if ((wr == null) || (wr.Ecb != IntPtr.Zero))
    {
    throw;
    }
    if (zero != IntPtr.Zero)
    {
    UnsafeNativeMethods.SetDoneWithSessionCalled(zero);
    }
    if (exception is ThreadAbortException)
    {
    Thread.ResetAbort();
    }
    return 0;
    }
    }

      对于上面的代码,我觉得没有必要去深究,但是对于那些具有强烈好奇心的朋友除外J。基本上上面的代码完成下面两个任务:

      通过传入的ECB和iWRType创建一个叫做ISAPIWorkerRequest的对象:

      bool useOOP = iWRType == 1;
      wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP);
      然后调用HttpRuntime.ProcessRequestNoDemand(wr),通过将创建的ISAPIWorkerRequest的对象作为参数传入。
      HttpRuntime.ProcessRequestNoDemand的调用真正进入了ASP.NET Runtime Pipeline,这是一个相对复杂的过程。在这里我想简单说说ISAPIWorkerRequest这个重要class,ISAPIWorkerRequest是一个Abstract class,它已通过ECB创建基于当前Request的Context的信息,针对不同的IIS版本,具有不同的ISAPIWorkerRequest subclass,比如:ISAPIWorkerRequestOutOfProc(IIS 5.x), ISAPIWorkerRequestInProcForIIS6, ISAPIWorkerRequestInProcForIIS7。ProcessRequest通过ISAPI传入的iWRType来创建不同HttpWorkerRequest,从而屏蔽了不同IIS的差异,后续的步骤就不需要考虑这种差异了,这是Abstract Factory的典型用法。
  • 相关阅读:
    帝国cms字母导航功能制作教程
    HTML 学习
    DOM
    C# DataTable 使用原创
    GridView中编辑状态下实现DropDownList默认值(原创)
    C#精髓 GridView72大绝技(清清月儿)
    SQL注入攻击<收藏>
    Web网页安全色谱<收藏>
    GridView根据linkButton值不同跳转不同页面(原创)
    解决"Failed to access IIS metabase"
  • 原文地址:https://www.cnblogs.com/waw/p/2162703.html
Copyright © 2011-2022 走看看