遗留问题

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

通过上面的图表可看出,每个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.NET 运行时详解 揭开请求过程神秘面纱

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

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

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

  3. iOS 运行时详解

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

  4. ASP.NET 运行机制详解

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

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

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

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

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

  7. ASP.NET 操作Cookie详解 增加,修改,删除

    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. 极简MVC的实现

    我们来打造一个简单的专用于json调用的mvc实现,最终会将如下的C#代码暴露给js调用(代码在最后面有下载): public class UserController { public static ...

  2. Redmine性能优化方案

    近来公司redmine服务器表现很糟糕,在16核,64GRAM的机器上,压测结果竟然只有每秒5~7个请求,部分页面一个都出不来. 以下是我对Redmine性能优化方案: redmine服务器性能问题排 ...

  3. linux 文件夹说明,用户添加删除,不熟悉的命令

    一.Linux 根目录下的文件夹说明 usr 程序默认安装路径,相当于windows的 program 附显示当前所处位置:pwd 二.用户 用户添加:useradd 用户名 passwd 用户名 u ...

  4. Asp.net Core中使用NLog,并封装成公共的日志方法

    1.安装NLog "NLog.Extensions.Logging": "1.0.0-rtm-alpha4" 2.配置NLog public void Conf ...

  5. 在线制作h5——上帝的礼物

    在线制作h5 网址:http://www.godgiftgame.com 网站名称:上帝的礼物 推荐指数:5颗星 功能概要 可以设置背景.元素图片.元素文字.元素图形.声音.加载.链接.分享,生成h5 ...

  6. VC++ 学习笔记(序):神一样的语言

    总的来说,我觉得VC++是一门神一样的语言——它是公认最强大.最复杂的:它一切以效率为第一要务,却又不肯落伍,拼命兼容现在的新的语言设计特点.本来在别的语言很容与就避开的问题,在这里要用很高的技巧去设 ...

  7. 英語版Windows Server 2012 R2を日本語化する手順

    [スタート]ボタンを押し.[Control Panel]を起動 [Clock, Language and Region]の下の[Add a Language]をクリック [Add a Language ...

  8. javascript不用new关键字创建对象示例

    <!doctype html> <html> <head> <meta charset="utf-8"> <title> ...

  9. ci配置smarty手记

    需要用ci来写一个后台配置smarty,在网络上能够找到一些相关的文章.但是都是比较旧的内容,大部分是smary2.*的配置方法.按照这个配置后会出现一些错误.其实配置看smary官方会比较简单. 基 ...

  10. 对C#对象的Shallow、Deep Cloning认识【转】

    好像园内比较多博客对 Shallow.Deep Cloning的翻译是深拷贝.浅拷贝,当时我懵了,这个叫法怎么怪怪的. 就好像看军情观察室,台湾评论员,导弹叫飞弹. 至于它们的区别,一张图就可以解释. ...