• ASP.NET内幕 ISAPI和应用程序域之间的桥梁


    介绍
    在前一篇,也是这一系列的第一篇中,我介绍了web服务器接收到web请求之后进行的第一步处理,以及如果被确定为ASP.NET资源请求时如何路由请求。你已经明白不同的IIS版本在处理ASP.NET相关请求时的差异,最终请求被分发到一个叫做aspnet_isapi.dll的非托管Win32组件,这个组件的作用就是web服务器和托管ASP.NET架构之间的桥梁。
    这篇文章中我将继续讨论上一篇中的遗留问题,开始研究处理环境的托管部分,在前一篇文章中它被称作ASP.NET运行时环境,作为一个黑盒子提到过。
    注意:ASP.NET运行时环境有时也叫做ASP.NET管道、Http运行时管道,或者是这些单词的混合。
    从aspnet_isapi.dll扩展到托管世界
    前一篇文章中我解释了IIS 5和IIS 6怎样管理ASP.NET请求,忽略它们怎样处理工作进程的创建、管理和回收,最后所有与请求相关的讯息归结到了aspnet_isapi.dll扩展上。
    这是非托管和托管世界的桥梁,也是所有ASP.NET架构中最缺乏文档的部分。
    注意:在写这篇文章的时候,IIS 7以一个上线许可与Longhorn Server Beta 3一起发布了。IIS 7中许多事情会改变,尽管这些前期文章只是关注之前的IIS版本,以后的文章将会致力于IIS 7中引入的改进。
    因为在这个主题的很多方面缺乏可用的文档,我将进行的解释可能不完全正确,特别是涉及到非托管结构的部分。然而我关于未文档化部分主题的考量,是基于一些对理解框架内部工作原理很有帮助的工具进行的:

    回到非托管和托管世界的桥梁上来,不管是IIS 6处理模式的情况下通过ISAPI扩展,还是IIS5处理模式的情况下通过工作进程,在CLR加载之后,黑暗魔法出现了。ASP.NETISAPI组件通过位于System.Web.Hosting命名空间中的两个托管类,调用一大把的非托管COM接口,通过COM接口方式暴露这些方法的类是AppManagerAppDomainFactory和ISAPIRuntime。
    注意:公共语言运行时CLR掌管每一个.NET应用程序的执行环境,它为运行托管应用程序提供环境和服务。CLR必须运行在Win32进程中,ASP.NET是.NET框架为CLR提供的一个宿主之一,确切地说在ASP.NET中,ASP.NET工作进程(IIS 5中为aspnet_wp.exe,IIS6中为w3wp.exe)就是运行CLR的进程。
    在研究这些类中发生的交互行为的技术细节之前,我们先大致看一下处理请求时究竟发生了什么。我前面已经介绍过这两个类,因为处理请求的入口点可以大致的分为两个方面。
        1. 如果AppDomain还不存在则创建APPDomain,将AppDomain指派给与请求对应的应用程序,这通过AppManagerAppDomain类实现。
        2. 提交和处理请求,由ISAPIRuntime类实现。
    这两个方面都很重要,第一个包括了不需要开发者参与的一些交互动作,主要涉及应用程序的运行环境,而第二个则是结构上最可配置的部分,我将在这个系列的后续文章中完整地探讨。
    从另一个角度来看,第一步包含的操作在应用程序生存期里只执行一次,就是说在启动的时候,而第二步包含的交互在每一次定位请求的目标应用程序时都会出现。
    创建AppDomain
    在前面文章中了解到,一个ASP.NET应用程序被封装成一个叫做应用程序域的实体,简写AppDomain,由ASP.NET架构中的一个类AppDomain表示。当特定应用程序的请求到达时,如果不存在则必须创建AppDomain对象,这通常发生在属于特定应用程序的请求第一次到达时,或者由于某种原因相应的AppDomain被关闭掉了,几个可能导致这个问题的原因我在后面会谈到。注意一个ASP.NET应用程序只会有一个AppDomain存在,它跟IIS应用程序一对一映射,可能基于物理目录或者虚拟目录创建。那么这个类的实例是怎样创建的呢?
    图1:创建AppDomain实例时由JetBrains dotTrace Profiler产生的调用栈

    使用Reflector可以推断AppDomain有一个抛出NotSupportedException异常的私有构造器,因此很明显这个方法行不通。实际上初始化AppDomain实例的入口点是AppManagerAppDomainFactory.Create方法,为了找出这一点,需要使用一个分析器跟踪初始化对象时生成的调用栈。图1显示了JetBrains dotTracProfiler的截屏,它显示了运行在IIS上的一个web应用程序在初始化AppDomain时生成的调用栈。有很多类参与了这个过程,它们也许是开发者永远不需要用到的。
    注意:AppManagerAppDomainFactory类在CLR初始化过程中只运行一次,就像它的名字暗示的,它作为创建AppDomain以及其他重要对象的入口点。
    图2显示了一个跟前面图1一样的调用栈,这次是从微软CLR Profiler调用树的输出中截取的,它为AppDomain的初始化提供了更多的信息。实际上高亮行表明主线程创建了两个AppDomain类的实例。
    图2:微软CLR Profiler调用树视图显示的创建AppDomain实例时的调用栈

    那么是怎样创建这个额外的实例呢,为什么创建?很不幸这不是个容易的问题。前面图中显示的调用栈,指示了在创建与实际执行应用程序相关的AppDomain实例时采用的步骤,因此这个额外的实例,看起来像是用于某些难于理解的目的的辅助对象,尽我所能的猜测,它被用于容纳所有不属于任何应用程序的对象,例如AppManagerAppDomainFactory,因此被放置在一个隔离的AppDomain中。对于AppManagerAppDomainFactory类,这个辅助AppDomain只在CLR初始化时实例化一次,它在CLR初始化很早的时候进行,如图3所示。
    图3:额外AppDomain实例的顺序和分配方法

    图3展示了Red Gate ANTSProfiler的一个截屏,它显示了额外AppDomain实例在CLR初始化很早的时候创建,很明显它比AppManagerAppDomainFactory类更早创建,因为很有可能它是AppManagerAppDomainFactory的容器。实际上在前面分析会话图示中,单例AppManagerAppDomainFactory实例的ID为52。
    另一个查看前面提到的两个AppDomain实例初始化过程的方法,是微软CLR Profiler提供的分配视图,它创建一个图形流程,展示处理中类的使用位置,像图4显示那样。
    图4:微软CLR Profiler分配视图显示的两个AppDomain初始化过程

    它展示了一个平面视图,从根元素,即CLR,和线程类之间裁剪出所有的类,然而它清晰的显示了创建两个AppDomain时的调用顺序。
    从前面图中可以得到另外一个关于AppDomain实例的信息是,每个实例占用100字节内存。这不会带来多少问题,但有人会认为AppDomain是一个很大的类,因为它必须容纳整个应用程序。实际上它提供很多的服务,但并不存储太多数据。
    到现在辅助AppDomain实例已经被创建了,包括AppManagerAppDomainFactory类,它的Create方法就是图1和2中显示的调用栈的入口方法。
    这个方法通过COM暴露出来,提供给调用者,因此是非托管代码。AppManagerAppDomainFactory类实现了 IAppManagerAppDomainFactory接口,它的结构显示在图5中。
    图5:IAppManagerAppDomainFactory接口结构

    这个接口使用ComImport和InterfaceType属性修饰,它们将类型绑定到非托管接口类型上。调用这个方法时,将产生图1和2的调用栈图形,最终触发AppDomain类实例的创建,容纳用于处理请求的目标应用程序。
    AppDomain对象创建以及运行之后,剩余工作就是处理请求,这比前面已经完成的工作内容更多,这也许是ASP.NET架构中最有意思的部分,但在这个有趣的部分开始之前我已经介绍了一些关键的主题。
    总结
    这篇文章中我介绍了ASP.NET基础结构中一些很底层的主题,涉及那些由ASP.NETISAPI扩展展示的非托管世界,以及运行web应用程序的AppDomain所展示的托管世界之间的一些接口。我展示了初始化AppDomain的机制,以及哪些类参与了这个处理。下一篇文章中我会通过HTTP管道讨论托管代码,在我的映像里这应当是ASP.NET架构最吸引人的一章,实际上它使得ASP.NET不同于其他所有的web开发框架。我希望你像我写这篇文章时一样充满兴趣的阅读,我的建议是打开一些分析工具自己尝试这些内容,这是充分理解它怎样工作的最好方法。
  • 相关阅读:
    基于MySQL提供的Yum repository安装MySQL5.6
    CentOS中无法使用setup命令 -bash:setup: command not found
    jdk8新特性-亮瞎眼的lambda表达式
    Git branch 分支与合并分支
    (转)Hashtable与ConcurrentHashMap区别
    java.lang.ClassNotFoundException: org.hibernate.engine.FilterDefinition的解决方案
    一些面试问题以及一些解法
    ipython的使用
    复习点算法知识,水仙花数加冒泡排序,以及一道算法题
    一些部署django用到的linux命令
  • 原文地址:https://www.cnblogs.com/answercard/p/1368663.html
Copyright © 2020-2023  润新知