博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ASP.NET运行时详解 生命周期入口分析
阅读量:6330 次
发布时间:2019-06-22

本文共 8840 字,大约阅读时间需要 29 分钟。

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

生命周期入口HttpRunTime


    在HttpRunTime中Http请求的入口处理流程比较复杂,说简单一点就是为后面的处理流程提供两个参数:IIS7WorkerRequest和HttpContext。这两个参数生成后直接调用HttpRuntime.ProcessRequestNotification(wr, httpContext)方法,执行Http请求的整个流程。

    所以,我们从ProcessRequestNotificationPrivate方法开始分析。代码如下:

private RequestNotificationStatus ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context)        {            RequestNotificationStatus pending = RequestNotificationStatus.Pending;            if (context.NeedToInitializeApp())            {                context.Response.InitResponseWriter();                applicationInstance = HttpApplicationFactory.GetApplicationInstance(context);                HttpApplication application = applicationInstance as HttpApplication;                if (application != null)                {                    application.AssignContext(context);                }            }            wr.SynchronizeVariables(context);            if (context.ApplicationInstance != null)            {                if (context.ApplicationInstance.BeginProcessRequestNotification(context, this._requestNotificationCompletionCallback).CompletedSynchronously)                {                    pending = RequestNotificationStatus.Continue;                }            }            else if (applicationInstance != null)            {                applicationInstance.ProcessRequest(context);                pending = RequestNotificationStatus.FinishRequest;            }            else            {                pending = RequestNotificationStatus.Continue;            }            if (pending != RequestNotificationStatus.Pending)            {                this.FinishRequestNotification(wr, context, ref pending);            }            return pending;        }

    这个方法里边包含的主要流程有:

    1. 初始化运行环境:EnsureFirstRequestInit。确认应用是否是第一次请求,如果是需要初始化运行环境,包括初始化Configuration、应用程序池、加载Bin下的Dll等。
    2. 初始化Response对象:context.Response.InitResponseWriter。
    3. 初始化Application对象:HttpApplicationFactory.GetApplicationInstance(context)。
    4. 执行请求过程:context.ApplicationInstance.BeginProcessRequestNotification。
    5. 完成请求通知:FinishRequestNotification。
    对于上面的主要流程,我用流程图表示可能更直观些。流程图如下所示:

   

    知道了上面的5个主要步骤,接下来就详细的介绍每个步骤执行的具体内容。

初始化运行环境


      通过执行EnsureFirstRequestInit方法来初始化我们整个应用的运行环境,那具体初始化了哪些操作?我们先查看下方法的源代码:

private void EnsureFirstRequestInit(HttpContext context)        {            if (this._beforeFirstRequest)            {                lock (this)                {                    if (this._beforeFirstRequest)                    {                        this._firstRequestStartTime = DateTime.UtcNow;                        this.FirstRequestInit(context);                        this._beforeFirstRequest = false;                        context.FirstRequest = true;                    }                }            }        }

    代码就那么寥寥几行,很明显的看出源代码里边最主要的一行就是:this.FirstRequestInit(context)。也就是说初始化运行环境实际是在这个方法里边执行的。我把FirstRequestInit方法里边的代码简单归纳了一下,下面是比较核心的代码流程:

    1. 初始化WEB配置:InitHttpConfiguration()。
    我们经常从ConfigurationManager的AppSettings和ConnectionStrings读取配置信息,这些配置信息就是InitHttpConfiguration方法从IIS环境和WEB环境下读取到内存提供给程序使用。
    2. 临时文件夹写权限判断:CheckAccessToTempDirectory()。
    简单理解就是整个WEB环境在运行时需要保存某些文件到临时文件夹里边,所以必须保证我们的WEB环境有临时文件夹的写权限。
    3. 初始化请求队列:InitRequestQueue()。
    按照服务器CPU环境自动或手动配置请求进程队列的限制,如果需要手动配置可在Web.config中配置ProcessModelSection部分。
    4. 健康监测系统运行环境:HealthMonitoringManager.StartHealthMonitoringHeartbeat()。
    启动一个计时器,定时检测编译错误、未处理异常、登录失败等信息,并记录到Windows事件日志、SQL Server数据库以及Trace输出窗口等。我们可以通过在Web.config中配置HealthMonitoringSection模块来初始化检测机制。
    5. 约束IIS文件夹:RestrictIISFolders(context)。
    执行ISAPI接口的函数(具体是执行什么操作,目前还不清楚)。
    6. 加载Bin下面的程序集:PreloadAssembliesFromBin()。
    加载Bin以及Bin下的子目录的所有程序集。
    7. 请求标头检查,防止注入攻击:this.InitHeaderEncoding()。

    运行环境初始化过程流程图如下:

   

初始化Response对象


    该步骤就一个操作,初始化我们常用到的Reponse.Output输出对象。

初始化Application对象


    执行HttpApplicationFactory工厂的GetApplicationInstance方法初始化Application对象。先把代码贴出来:

internal static IHttpHandler GetApplicationInstance(HttpContext context)        {             //…省略其他代码            _theApplicationFactory.EnsureInited();            _theApplicationFactory.EnsureAppStartCalled(context);            return _theApplicationFactory.GetNormalApplicationInstance(context);        }

    代码第一行执行工厂的EnsureInited方法,获取global.asax文件所在路径,然后编译它。编译后启动检测,查看global.asax文件是否有更新,如果有更新WEB应用会被停止。

    代码第二行执行工厂方法EnsureAppStartCalled(context),创建特别的HttpApplication实例,触发ApplicationOnStart事件,执行global_asax中的Application_Start事件,新建的特别的HttpApplication实例在处理完事件后就被回收。
    代码第三行执行工厂方法GetNormalApplicationInstance(context),创建我们在WEB应用中经常用到的Application对象。既然是我们经常用到的Application对象,那么GetNormalApplicationInstance方法里边应该不仅仅是简单的创建步骤。我们先看下GetNormalApplicationInstance方法的代码:

private HttpApplication GetNormalApplicationInstance(HttpContext context)        {            HttpApplication state = null;            lock (this._freeList)            {                if (this._numFreeAppInstances > 0)                {                    state = (HttpApplication)this._freeList.Pop();                    this._numFreeAppInstances--;                    if (this._numFreeAppInstances < this._minFreeAppInstances)                    {                        this._minFreeAppInstances = this._numFreeAppInstances;                    }                }            }            if (state == null)            {                state = (HttpApplication)HttpRuntime.CreateNonPublicInstance(this._theApplicationType);                using (new ApplicationImpersonationContext())                {                    state.InitInternal(context, this._state, this._eventHandlerMethods);                }            }            return state;        }

    这段代码我们必须得分析下,这里包含了我们经常说的“应用程序池“,体现在代码里边就是_freeList集合,在获取Application对象时,先从_freeList查找空闲的Application对象,如果有就直接返回一个空闲对象;如果没有则执行HttpRuntime.CreateNonPublicInstance(this._theApplicationType)方法创建一个Application对象,创建之后调用InitInternal方法执行初始化操作。

    InitInternal方法比较重要,它初始化了我们的HttpModules,并且构建了Http请求的20多个管道执行步骤。InitInternal方法执行的具体内容比较庞大,我会单独分一个章节来介绍。但要提出一点,该方法为我们创建了具体的管道管理对象StepManager,该对象在下一个”执行请求过程“步骤中会用到。

执行请求过程


    初始化操作执行完了就该处理我们的请求了,Application对象的BeginProcessRequestNotification方法包含了请求的处理过程。看下代码:

internal IAsyncResult BeginProcessRequestNotification(HttpContext context, AsyncCallback cb)        {            if (this._context == null)            {                this.AssignContext(context);            }            context.CurrentModuleEventIndex = -1;            HttpAsyncResult result = new HttpAsyncResult(cb, context);            context.NotificationContext.AsyncResult = result;            this.ResumeSteps(null);            return result;        }

    方法创建HttpAsyncResult对象,保存了我们的回调事件。等ResumeSteps方法执行完后再回调HttpAsyncResult的事件。接下来看看ResumeSteps方法的具体内容。代码比较简单,如下所示:

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

    代码里边有一个_stepManager对象,在上一个步骤“初始化Application对象”中我们有提到StepManager,StepManager实体对象也是在那个步骤中被创建的。ResumeSteps方法启动了Http请求执行管道的20多个步骤。那么,StepManager到底包含哪些内容?我们查看下他的定义:

internal abstract class StepManager        {            internal abstract void BuildSteps(WaitCallback stepCallback);            internal abstract void InitRequest();            internal abstract void ResumeSteps(Exception error);        }

    StepManager是一个抽象类,定义了三个抽象方法,BuildSteps(构建步骤)和ResumeSteps(执行步骤)我们已经知道了,InitRequest用来初始化请求信息。StepManager总共有两个实现类:ApplicationStepManager和PipelineStepManager。ApplicationStepManager是IIS6以及IIS7经典模式执行步骤管理对象,PipelineStepManager是IIS7的执行步骤管理

对象。这两个对象会在下一章节详细介绍。

完成请求通知


    FinishRequestNotification主要做善后工作,代码如下:

private void FinishRequestNotification(IIS7WorkerRequest wr, HttpContext context, ref RequestNotificationStatus status)        {            HttpApplication applicationInstance = context.ApplicationInstance;            context.ReportRuntimeErrorIfExists(ref status);            IntPtr requestContext = wr.RequestContext;            bool sendHeaders = UnsafeIISMethods.MgdIsLastNotification(requestContext, status);            try            {                context.Response.UpdateNativeResponse(sendHeaders);            }            catch (Exception exception)            {            }            if (sendHeaders)            {                context.FinishPipelineRequest();            }            if (status != RequestNotificationStatus.Pending)            {                PipelineRuntime.DisposeHandler(context, requestContext, status);            }        }

    从代码可以看出FinishRequestNotification方法记录运行时异常,调用context.FinishPipelineRequest方法完成整个请求、释放Request、Response以及Application对象。

总结


    通过上面几个步骤的分析,请求的入口是在HttpRuntime。第一次Http请求发生后,HttpRuntime初始化整个WEB环境,然后通过HttpApplication的工厂类HttpApplicationFactory创建HttpApplication。HttpApplication实体对象通过StepManager的BuildSteps方法构建请求管道(Pipeline)步骤,然后通过ResumeSteps方法执行管道所有的事件。

    下一章节主要介绍管Pipeline步骤的构建和执行细节以及页面的生命周期,敬请期待!

转载于:https://www.cnblogs.com/w-wanglei/p/aspnet-runtime1.html

你可能感兴趣的文章
波形捕捉:(3)"捕捉设备"性能
查看>>
AliOS Things lorawanapp应用介绍
查看>>
美国人的网站推广方式千奇百怪
查看>>
java web学习-1
查看>>
用maven+springMVC创建一个项目
查看>>
linux设备驱动第四篇:以oops信息定位代码行为例谈驱动调试方法
查看>>
redis知识点整理
查看>>
Hello World
查看>>
Spring3全注解配置
查看>>
ThreadLocal真会内存泄露?
查看>>
IntelliJ IDEA
查看>>
低版本mybatis不能用PageHeper插件的时候用这个分页
查看>>
javaweb使用自定义id,快速编码与生成ID
查看>>
[leetcode] Add Two Numbers
查看>>
elasticsearch suggest 的几种使用-completion 的基本 使用
查看>>
04-【MongoDB入门教程】mongo命令行
查看>>
Hadoop HA元数据备份
查看>>
字符串与整数之间的转换
查看>>
断点传输HTTP和URL协议
查看>>
redis 数据类型详解 以及 redis适用场景场合
查看>>