ASP.NET运行时详解 集成模式和经典模式

时间:2022-09-20 19:36:57

遗留问题

《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的管道流程执行具体的请求处理流程。整个的流程如下图所示:

ASP.NET运行时详解 集成模式和经典模式

2. IIS 7 模式

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

ASP.NET运行时详解 集成模式和经典模式

在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 = ; 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>>。什么意思呢?我们先看下面的图:

ASP.NET运行时详解 集成模式和经典模式

通过上面的图表可看出,每个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 > );
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 = ; 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应用作哪些扩展。

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

ASP.NET运行时详解 集成模式和经典模式的更多相关文章

  1. ASP&period;NET 运行时详解 揭开请求过程神秘面纱

    对于ASP.NET开发,排在前五的话题离不开请求生命周期.像什么Cache.身份认证.Role管理.Routing映射,微软到底在请求过程中干了哪些隐秘的事,现在是时候揭晓了.抛开乌云见晴天,接下来就 ...

  2. ASP&period;NET运行时详解 生命周期入口分析

    说起ASP.NET的生命周期,网上有很多的介绍.之前也看了些这方面的博客,但我感觉很多程序猿像我一样,看的时候似乎明白,一段时间过后又忘了.所以,最近Heavi花了一段时间研究ASP.NET的源代码, ...

  3. iOS 运行时详解

    注:本篇文章转自:http://www.jianshu.com/p/adf0d566c887 一.运行时简介 Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行 ...

  4. ASP&period;NET 运行机制详解

    1.浏览器和服务器的交互原理 通俗描述:我们平时通过浏览器来访问网站,其实就相当于你通过浏览器去访问一台电脑*问文件一样,只不过浏览器的访问请求是由被访问的电脑上的一个 WEB服务器软件来接收处理, ...

  5. LVS原理详解(3种工作模式及8种调度算法)

    2017年1月12日, 星期四 LVS原理详解(3种工作模式及8种调度算法)   LVS原理详解及部署之二:LVS原理详解(3种工作方式8种调度算法) 作者:woshiliwentong  发布日期: ...

  6. 如何利用IIS调试ASP&period;NET网站程序详解

    如何利用IIS调试ASP.NET网站程序详解 更新时间:2019年01月13日 08:44:13   作者:江湖逍遥    我要评论   这篇文章主要给大家介绍了关于如何利用IIS调试ASP.NET网 ...

  7. ASP&period;NET&&num;160&semi;操作Cookie详解&&num;160&semi;增加,修改,删除

    ASP.NET 操作Cookie详解 增加,修改,删除 Cookie,有时也用其复数形式Cookies,指某些网站为了辨别用户身份而储存在用户本地终端上的数据(通常经过加密).定义于RFC2109.它 ...

  8. Net is as typeof 运行运算符详解 net 自定义泛型那点事

    Net is as typeof 运行运算符详解   概述 在了解运行运算符的前提我们需要了解什么是RTTI ,在任何一门面向对象的语言中,都有RTTI这个概念(即 运行时). RTTI(Run-Ti ...

  9. JVM运行原理详解

    1.JVM简析:      作为一名Java使用者,掌握JVM的体系结构也是很有必要的.      说起Java,我们首先想到的是Java编程语言,然而事实上,Java是一种技术,它由四方面组成:Ja ...

随机推荐

  1. Objective-C中block的底层原理

    先出2个考题: 1. 上面打印的是几,captureNum2 出去作用域后是否被销毁?为什么? 同样类型的题目: 问:打印的数字为多少? 有人会回答:mutArray是captureObject方法的 ...

  2. (转)Unity AssetBundle爬坑手记

    转自:http://www.cnblogs.com/ybgame/p/3973177.html 这篇文章从AssetBundle的打包,使用,管理以及内存占用各个方面进行了比较全面的分析,对Asset ...

  3. CAlayer层的属性

    iOS开发UI篇—CAlayer层的属性 一.position和anchorPoint 1.简单介绍 CALayer有2个非常重要的属性:position和anchorPoint @property ...

  4. ubuntu桌面环境配置及切换

    修改ubuntu默认启动的桌面环境:Ubuntu是否启动图形化界面取决于default-display-manager的设置. vi /etc/X11/default-display-manager值 ...

  5. verilog中&equals;和&lt&semi;&equals;的区别

    一般情况下使用<=,组合逻辑使用=赋值,时序逻辑使用<=赋值: 举个例子:初始化m=1,n=2,p=3:分别执行以下语句 1.begin m=n:n=p:p=m: end 2.begin ...

  6. codefoces384A-Mafia心得

    题目描述:One day n friends gathered together to play "Mafia". During each round of the game so ...

  7. BZOJ&lowbar;4016&lowbar;&lbrack;FJOI2014&rsqb;最短路径树问题&lowbar;最短路&plus;点分治

    BZOJ_4016_[FJOI2014]最短路径树问题_最短路+点分治 Description 给一个包含n个点,m条边的无向连通图.从顶点1出发,往其余所有点分别走一次并返回. 往某一个点走时,选择 ...

  8. weakhashmap简单理解

    map中的key(注意String,和元数据作key有特殊性),gc后会被立马干掉, key被干掉后,其对应的entry将被存入queue中 /** * Reference queue for cle ...

  9. mysql与linux ~ 磁盘分析与调优

    一 简介 谈谈磁盘IO的问题二 目的:如何进行IO性能问题的排查 二  linux角度   一 机械硬盘基本定义       寻道时间,表示磁头在不同磁道之间移动的时间(最耗时).       旋转延 ...

  10. web全栈架构师&lbrack;笔记&rsqb; — 02 数据交互

    数据交互 一.http协议 基本特点 1.无状态的协议 2.连接过程:发送连接请求.响应接受.发送请求 3.消息分两块:头.体 http和https 二.form 基本属性 action——提交到哪儿 ...