zoukankan      html  css  js  c++  java
  • ASP.NET运行时详解 集成模式和经典模式

    遗留问题

        在《ASP.NET运行时详解 生命周期入口分析》中遗留两个问题,包括Application的InitInternal方法执行细节、IIS6和II7经典模式请求管道管理类ApplicationStepManager和IIS7请求管道管理类PipelineStepManager的实现细节。这两个问题贯穿了整个ASP.NET运行过程。所以,要把ASP.NET运行过程了解清楚,这两个问题不得不解决。
        为了大家更容易切入该篇的内容,我们先回顾下这两个问题:

        1. Application的InitInternal方法
        上一篇“初始化Application对象”章节中,HttpApplicationFactory工厂创建Application对象的方法GetApplicationInstance中有这么一段代码:

    private HttpApplication GetNormalApplicationInstance(HttpContext context)
            {
                //
                if (state == null)
                {
                    state = (HttpApplication)HttpRuntime.CreateNonPublicInstance(this._theApplicationType);
                    using (new ApplicationImpersonationContext())
                    {
                        state.InitInternal(context, this._state, this._eventHandlerMethods);
                    }
                }
                return state;
            }

        代码中HttpApplication实体对象state调用InitInternal方法执行初始化操作。

        2. StepManager的两个实现类ApplicationStepManager和PipelineStepManager

        上一篇“执行请求过程”章节中,有介绍HttpApplication类的ResumeSteps方法,代码如下:

    private void ResumeSteps(Exception error)
            {
                this._stepManager.ResumeSteps(error);
            }

        ResumeSteps方法调用StepManager的ResumeSteps方法执行ASP.NET运行的管道步骤。但在最初时,ASP.NET运行管道肯定是空的。那么,又由谁来构建ASP.NET运行管道?这里就提到第一个问题中的InitInternal方法。

    预备知识

        在解决上节的两个问题之前,我们先得了解下IIS 6以及IIS 7经典模式和IIS 7在执行请求过程中的一些区别,ASP.NET 5是兼容了这两种模式。这也就是为什么StepManager有两个实现类,ApplicationStepManager对应IIS6以及IIS 7经典模式,而PipelineStepManager对应IIS 7的集成模式。下面分别介绍这两种模式。

        1. IIS 6以及IIS 7经典模式

        早期的IIS版本中,IIS接收到一个请求时先判断请求类型,如果是静态文件直接由IIS处理;如果是一个ASP.NET请求类型,IIS把请求发送给IIS的扩展接口ASP.NET ISAPI DLL。ISAPI相当于ASP.NET应用的容器,当他接收到ASP.NET类型的请求后,启动ASP.NET的管道流程执行具体的请求处理流程。整个的流程如下图所示:

        2. IIS 7 模式

        IIS 7和之前的版本区别比较大,IIS7直接把ASP.NET的运行管道流程集成到了IIS上。先看下IIS7从接收请求到请求处理完毕的流程图:

        在IIS7中,ASP.NET请求处理管道Pipeline直接覆盖了IIS本身的管道Pipeline,IIS整个流程按照ASP.ENT管道流程执行,例如BeginRequest、AuthenticateRequest、…、EndRequest。而不是通过插件扩展(ISAPI)形式,不同的内容(jpg、html、php等)通过不同的插件来执行。
        IIS7的优势体现在哪里?开发人员可以实现自定义的HttpModule或者HttpHandler,然后直接集成到IIS上,例如,我可以自定义一个JpgHttpHandler,然后通过IIS的Handler部署方式,把JpgHttpHandler部署到IIS上,处理所有的请求格式为*.jpg的请求。
    至于怎样把自定义的HttpModule或者HttpHandler部署到IIS上并处理指定格式的请求。这里我就不详细介绍了,感兴趣的同学可在评论反馈下,我根据情况,再单独开一篇详细介绍IIS7。

    ASP.NET执行管道初始化

       前面提到了Application的InitInternal方法,那么InitInternal方法中具体包括哪些操作?看看经过裁剪后的代码:

    internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers)
            {
                using (new DisposableHttpContextWrapper(context))
                {
                    if (HttpRuntime.UseIntegratedPipeline)
                    {
                        this.InitIntegratedModules();
                        goto Label_006B;
                    }
                    this.InitModules();
                    Label_006B:
                    if (handlers != null)
                    {
                        this.HookupEventHandlersForApplicationAndModules(handlers);
                    }
                }
                if (HttpRuntime.UseIntegratedPipeline && (this._context != null))
                {
                    this._context.HideRequestResponse = false;
                }
                this._resumeStepsWaitCallback = new WaitCallback(this.ResumeStepsWaitCallback);
                if (HttpRuntime.UseIntegratedPipeline)
                {
                    this._stepManager = new PipelineStepManager(this);
                }
                else
                {
                    this._stepManager = new ApplicationStepManager(this);
                }
                this._stepManager.BuildSteps(this._resumeStepsWaitCallback);
            }

        代码中,HookupEventHandlersForApplicationAndModules方法接收了一个handlers参数,handler中存放了Application的所有Application事件,所有HookupEventHandlersForApplicationAndModules的工作就是遍历handlers事件,分别执行。我们通过代码可看到有两处都用到HttpRuntime的UseIntegratedPipeline作为判断条件。UseIntegratedPipeline用来判断是否是集成模式,也就是我们上面提到的IIS7模式。
        第一处判断,如果是集成模式就调用InitIntegratedModules方法初始化Module配置;如果是经典模式就调用InitModules方法初始化Module配置。
        第二处判断,如果是集成模式,_stepManager实例化为PipelineStepManager类型;如果是经典模式,_stepManager实例化为ApplicationStepManager类型。
        最后一段代码调用BuildSteps方法构建执行步骤到ASP.NET运行管道事件上。
        经过上面的分析,不管是集成模式还是经典模式,执行管道的初始化都包含两个步骤:初始化Module配置、构建执行步骤。接下来,我们分别按照集成模式和经典模式介绍这两个步骤。

    管道初始化-集成模式

        1. 初始化Module配置

        InitIntegratedModules方法的实现很简单,两行代码:

    private void InitIntegratedModules()
            {
                this._moduleCollection = this.BuildIntegratedModuleCollection(_moduleConfigInfo);
                this.InitModulesCommon();
            }

       

        第一行代码调用BuildIntegratedModuleCollection方法初始化_moduleCollection集合,这个方法遍历module配置集合,把每一个module配置保存到ModulesEntry对象,ModulesEntry存放了module名称和module的类型,它还提供了一个Create方法创建Module实体类。_moduleCollection存储了Module的名称和实体对象。先留一个问题:module配置集合_moduleConfigInfo的数据是从哪里来的?
        第二行代码调用InitModulesCommon方法初始化_moduleCollection集合中的每一个module。InitModulesCommon方法代码如下:

    private void InitModulesCommon()
            {
                int count = this._moduleCollection.Count;
                for (int i = 0; i < count; i++)
                {
                    t his._currentModuleCollectionKey = this._moduleCollection.GetKey(i);
                    this._moduleCollection[i].Init(this);
                }
                this.InitAppLevelCulture();
            }

        最主要的一行代码是_moduleCollection[i].Init(this),我们知道_moduleCollection存储了Module的实体对象。所以_moduleCollection[i]就是一个IHttpModule的实体对象。Init方法大家就该熟悉了,它把Module(例如,UrlAuthorizationModule、FormsAuthenticationModule等)的事件注册到执行管道流程事件(BeginRequest、ResolveRequestCache、EndRequest等)中。
        初始化Module完成后,Application中保存了_moduleIndexMap和ModuleContainers两个集合,_moduleIndexMap存储结构是HashTable<ModuleName,Index>,而ModuleContainers存储结构为Container<Index, List<Event>>。什么意思呢?我们先看下面的图:

    ModuleContainers

        通过上面的图表可看出,每个Module对应了一个管道列表,每个管道对应一个Events集合。到目前为止,我们已经为后面的“执行请求管道”提供了数据基础。现在我们再回看之前遗留的一个问题:module配置集合_moduleConfigInfo的数据是从哪里来的?
    我们之前说过IIS7已经集成了ASP.NET的运行管道,它把对应的Module和Handler添加到了自身的配置文件中,在IIS 7管理工具“管理->配置管理项”里,我们可以看到具体的配置信息。 Application类中提供了一个GetModuleCollection方法,它正是从IIS的配置文件中加载了所有的Module。
        总结下集成模式的管道初始化:Application先从IIS配置读取Module信息,然后把Module对应的事件存放到ModuleContainers集合。

        2. 构建执行步骤

        集成模式的StepManager的实现类是PipelineStepManager。之前介绍Application的InitInternal方法,它的最后一行代码调用了PipelineStepManager对象的BuildSteps方法。BuildSteps方法代码如下:

    internal override void BuildSteps(WaitCallback stepCallback)
            {
                HttpApplication app = base._application;
                HttpApplication.IExecutionStep step = new HttpApplication.MaterializeHandlerExecutionStep(app);
                app.AddEventMapping("ManagedPipelineHandler", RequestNotification.MapRequestHandler, false, step);
                app.AddEventMapping("ManagedPipelineHandler", RequestNotification.ExecuteRequestHandler, false, app.CreateImplicitAsyncPreloadExecutionStep());
                HttpApplication.IExecutionStep step2 = new HttpApplication.CallHandlerExecutionStep(app);
                app.AddEventMapping("ManagedPipelineHandler", RequestNotification.ExecuteRequestHandler, false, step2);
                HttpApplication.IExecutionStep step3 = new HttpApplication.TransitionToWebSocketsExecutionStep(app);
                app.AddEventMapping("ManagedPipelineHandler", RequestNotification.EndRequest, true, step3);
                HttpApplication.IExecutionStep step4 = new HttpApplication.CallFilterExecutionStep(app);
                app.AddEventMapping("AspNetFilterModule", RequestNotification.UpdateRequestCache, false, step4);
                app.AddEventMapping("AspNetFilterModule", RequestNotification.LogRequest, false, step4);
                this._resumeStepsWaitCallback = stepCallback;
            }

        BuildSteps方法添加几个执行步骤(IExecuteStep)到ModuleContainer中指定Module的某个管道的事件集合中。我们拿第三行代码举例,在添加之前创建一个step,然后找到Application的ModuleContainers集合中ModuleName为ManagedPipelineHandler的管道,最后根据传入的事件类型RequestNotification.ExecuteRequestHandler把step添加到对应管道的事件列表中。
        添加这些步骤有什么用?最主要的两个作用即是重定向(Remap)IHttpHandler、执行IHttpHandler的ProcessRequest方法。至于详细的说明,我们再下一个篇幅中再讲解。

    管道初始化-经典模式

       1. 初始化Module配置

        从上面我们已经了解到集成模式读取Module配置是从IIS读取,那经典模式又是从哪里读取配置?下看下经典模式初始化Module配置代码:

    private void InitModules()
            {
                HttpModuleCollection modules = RuntimeConfig.GetAppConfig().HttpModules.CreateModules();
                HttpModuleCollection other = this.CreateDynamicModules();
                modules.AppendCollection(other);
                this._moduleCollection = modules;
                this.InitModulesCommon();
            }

        通过第一行代码,不难看出Module配置是从RuntimeConfig中读取,也就是从machine.config配置中读取,而不是IIS。第二行代码是通过动态的方式提供Module,一般没有使用。最后一行代码执行InitModulesCommon方法,在上一节我们可看到该方法的定义。
        简而言之,经典模式的Module是从machine.config中读取。

        2. 构建执行步骤

        经典模式的StepManager的实现类是ApplicationStepManager。之前介绍Application的InitInternal方法,它的最后一行代码调用了ApplicationStepManager对象的BuildSteps方法。BuildSteps方法的代码有点多,但必须得全部粘出来:

    internal override void BuildSteps(WaitCallback stepCallback)
            {
                ArrayList steps = new ArrayList();
                HttpApplication app = base._application;
                bool flag = false;
                UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings;
                flag = urlMappings.IsEnabled && (urlMappings.UrlMappings.Count > 0);
                steps.Add(new HttpApplication.ValidateRequestExecutionStep(app));
                steps.Add(new HttpApplication.ValidatePathExecutionStep(app));
                if (flag)
                {
                    steps.Add(new HttpApplication.UrlMappingsExecutionStep(app));
                }
                app.CreateEventExecutionSteps(HttpApplication.EventBeginRequest, steps);
                app.CreateEventExecutionSteps(HttpApplication.EventAuthenticateRequest, steps);
                app.CreateEventExecutionSteps(HttpApplication.EventDefaultAuthentication, steps);
                app.CreateEventExecutionSteps(HttpApplication.EventPostAuthenticateRequest, steps);
                app.CreateEventExecutionSteps(HttpApplication.EventAuthorizeRequest, steps);
                app.CreateEventExecutionSteps(HttpApplication.EventPostAuthorizeRequest, steps);
                app.CreateEventExecutionSteps(HttpApplication.EventResolveRequestCache, steps);
                app.CreateEventExecutionSteps(HttpApplication.EventPostResolveRequestCache, steps);
                steps.Add(new HttpApplication.MapHandlerExecutionStep(app));
                app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps);
                app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps);
                app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps);
                app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps);
                steps.Add(app.CreateImplicitAsyncPreloadExecutionStep());
                steps.Add(new HttpApplication.CallHandlerExecutionStep(app));
                app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps);
                app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps);
                app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps);
                steps.Add(new HttpApplication.CallFilterExecutionStep(app));
                app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps);
                app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps);
                this._endRequestStepIndex = steps.Count;
                app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps);
                steps.Add(new HttpApplication.NoopExecutionStep());
                this._execSteps = new HttpApplication.IExecutionStep[steps.Count];
                steps.CopyTo(this._execSteps);
                this._resumeStepsWaitCallback = stepCallback;
            }

        通过上面的代码猜测,估计BuildSteps方法直接创建了执行管道的所有步骤。至于对不对,我们来分析分析代码。第一行创建了一个steps集合,它的集合成员有两种添加方式:一种是steps.Add,另外一种是app.CreateEventExecutionSteps。下面分别举例分析:

        1. steps.Add
        例如steps.Add(new HttpApplication.ValidateRequestExecutionStep(app)),直接给steps添加一个校验request请求的ValidateRequestExecutionStep步骤。
        2. app.CreateEventExecutionStep
        例如app.CreateEventExecutionSteps(HttpApplication.EventBeginRequest, steps),这里调用了Application的CreateEventExecutionSteps方法,代码如下:

    private void CreateEventExecutionSteps(object eventIndex, ArrayList steps)
            {
                AsyncAppEventHandler handler = this.AsyncEvents[eventIndex];
                if (handler != null)
                {
                    handler.CreateExecutionSteps(this, steps);
                }
                EventHandler handler2 = (EventHandler)this.Events[eventIndex];
                if (handler2 != null)
                {
                    Delegate[] invocationList = handler2.GetInvocationList();
                    for (int i = 0; i < invocationList.Length; i++)
                    {
                        steps.Add(new SyncEventExecutionStep(this, (EventHandler)invocationList[i]));
                    }
                }
            }

        从代码可以看出,Application把EventBeginRequest注册过的异步事件(AsyncEvents)、同步事件Events中的事件遍历,然后每一个事件创建一个同步或者异步的ExecutionStep步骤。最终我们的steps就相当于是一个过程化的链表,一个个事件串在一起按部就班的执行。
        这些步骤中也包括了想MapHandlerExecutionStep(定向Handler)、CallHandlerExecutionStep(执行Handler)步骤。对比集成模式,集成模式把管道Pipeline按结构化的形式存储在ModuleContainers中,而经典模式按过程化的形式直接把所有事件排列成一个列表。但不管是集成模式还是经典模式,最终执行管道的内容是一样的。只不过集成模式更加具有扩展性。上面有很多的ExecutionStep,每一个Step的作用是什么,由于篇幅问题,我在下一篇在作介绍。

    总结

        这一篇的内容主要围绕“遗留问题”章节中的Application的InitInternal方法、StepManager的实现两个问题做执行管道的介绍。由于涉及到IIS7的集成模式,所有我在“预备知识”中简单的介绍了集成模式和经典模式的区别。然后分别分析集成模式、经典模式中Module的初始化以及执行管道的构建的过程。

        本篇内容中也多次提到Module,Module也就是我们经常看到的IHttpModule,它可以在初始化的时候往我们的请求执行管道中添加自定义事件,在这些事件中我们可以做任何想做的事件,例如校验Request信息、验证身份、缓存处理、重定向IHttpHandler等。

    剩下的内容包括:经典模式和集成模式下管道具体是怎样执行的、管道中各个步骤执行的内容、页面的生命周期。这些内容我将会在下一篇中做详细介绍。另外,还有提到IIS 7集成模式,如果大家感兴趣我也可另起炉灶,分享下IIS 7到底发生了什么变化,以及我们可以使用IIS 7为我们的WEB应用作哪些扩展。

        如果本篇内容对大家有帮助,请关注博主。如果觉得不好,也欢迎拍砖。你们的反馈就是博主的动力!下篇内容,敬请期待!

  • 相关阅读:
    html框架
    head标签
    项目报错,tomcat中引起
    Eclipse中点击小猫提示Tomcat settings should be set in Tomcat Preference Page
    同一台电脑上安装两个tomcat服务器
    Java排序算法
    格式化系统日期
    九九乘法表
    Java实现给定字符串的倒序输出
    Eclipse警告:The serializable class XXX does not declare a static final serialVersionUID field of type long
  • 原文地址:https://www.cnblogs.com/w-wanglei/p/aspnet-runtime2.html
Copyright © 2011-2022 走看看