学习资料

    联系方式

    地址:郑州高新区莲花街100号
    电话:67756392
    邮箱:wg@haut.edu.cn

    学习资料

    您现在的位置: 中心首页 > 学习资料 > 正文

    浅谈Asp.net运行机制

    作者:张信秀 时间:2015年06月01日 15:07 点击:

    ASP.NET之旅——浅谈Asp.net运行机制

    很多Asp.net开发人员都有过Asp的背景,以至于我们开发程序的时候总是停留在“页面”层次思考,也就是说我们常常会只考虑我们现在所做的系统是要完成什么功能,是要做问卷调查网站还是个人网站,而很少在“请求级”思考,思考能不能通过编码的方式来操作一个Http请求。在跨入Asp.net后Asp有了质的飞跃,很多底层的Http请求都得到了良好的应用,这时候Asp.net不仅仅是一门开发语言,而是一个开发平台。想要能在“请求级”来实现编码,我们就不得不来说说Asp.net的内部运行机制。

    一、Asp.net顶层运行机制

    当客户端的Http请求到达服务器后,IIS会为Http请求分配应用程序池,在应用程序池中创建请求需要的管道,请求管道将Http请求的各步骤进行了分配。

    当第一次请求页面时,在请求管道中经过了身份验证等模块的一系列操作后,他会被映射处理程序处理,发现要请求的是.aspx页面,这时请求将转交给Asp.net执行也就是我们上图的步骤2。Asp.net页面分为前台.aspx文件和后台.cs文件,Asp.net引擎会将前台文件和后台文件合并生成一个页面(Page)类,然后再由编译器将该页面类编译成为程序集,再由程序集生成静态HTML页面,然后将HTML文件返回给映射处理程序,并将静态HTML页面返回给客户端浏览器解释运行。

    1. //Page 类与扩展名为 .aspx 的文件相关联。

    2. //表示从 ASP.NET Web 应用程序的宿主服务器请求的 .aspx 文件。 这些文件在运行时被编译为 Page 对象,并被缓存在服务器内存中。

    3. //Page类的属性中包含有Asp的所有对象,如:Application、Request、Response等对象

    4. //开发人员在页面编程中使用的对象也都是使用该类的属性,对于这些对象的产生将会在下面章节中讲到

    5. //Asp.net程序中创建的语法如下:

    6. public class Page : TemplateControl, IHttpHandler

    //Page 类与扩展名为 .aspx 的文件相关联。
    //表示从 ASP.NET Web 应用程序的宿主服务器请求的 .aspx 文件。 这些文件在运行时被编译为 Page 对象,并被缓存在服务器内存中。
    //Page类的属性中包含有Asp的所有对象,如:Application、Request、Response等对象
    //开发人员在页面编程中使用的对象也都是使用该类的属性,对于这些对象的产生将会在下面章节中讲到
    //Asp.net程序中创建的语法如下:
    public class Page : TemplateControl, IHttpHandler

    Note:生成的程序集一般会被放在服务器的C:/windows/Microsoft .NET/Framework/V4.0 303 19/Temporary ASP.NET Files目录下。

    当用户第二次请求该页面时,直接调用编译好的程序集即可,从而大大提高打开页面的速度。正因为如此,我们才会发现当用户第一次打开该页面时速度会很慢,但是以后再打开该页面速度会很快的原因。

    这里我们站在一个比较高层次上看Asp.net的运行机制,我们一步步的将HTTP请求转变成了我们通常可见的HTML页,但却忽略了很多细节。首先请求在进入管道后,HTTP请求是如何一步步转交给.aspx文件的,其次在页面编程时会用到很多Asp对象这些对象是如何创建的。不着急,我们将问题上抛,进入我们的第二节。

    二、IIS集成模块处理机制

    上节站在较高层次上解读了Asp.net的运行机制,请求过程很简单,核心的东西是Asp.net引擎,在第一次请求时它将请求的页面编译成为程序集,在以后的请求中只调用请求的程序集即可,大大提高了执行效率。在这里还要清楚一点,上图中的从步骤2往后的请求是在模块中实现的。那么何为请求模块呢,我们继续往下看。

    1、理解HttpModule模块

    在以前我们说过IIS7.0以后的版本中管道模式升级为了集成模式,在请求管道中我们可以任意指定请求的模块来实现我们的请求,同时为了实现某个功能开发人员也可以自己编写模块来把它集成到IIS中。这种模块其实封装了一个个的处理单元,当HTTP请求进入模块后,处理单元会根据请求信息来实现各自模块的功能,在处理完成后会把HTTP请求的信息返回到HttpHandler中。
    IIS中也自带了很多处理模块,如:下图中的身份验证的Anon模块、Static File模块、Default Document模块等。


    上图是HTTP请求进入W3WP.exe进程后的工作流程。

    Note:W3WP.exe它是一个工作进程,该进程实现了IIS和应用程序池的联接工作。如果有多个应用程序池在运行就会对应有多个W3WP.exe的进程实例运行。另外Managed Engines模块是.NET的驱动程序,它将HTTP请求从IIS的集成模式中连接到.NET Runtime。

    这张图上的请求可以看出Http请求在发送到IIS管道后,首先是由HttpModule的Authentication处理,处理完成后为请求授予权限,请求的信息有多种形式,至于几种特殊的请求方式我们将会在系列博文中讲到。HttpHandler来处理,HttpModule负责请求的身份验证及授权操作。

    Note:加载哪些 ASP.NET 模块(如 SessionStateModule)取决于应用程序从父应用程序继承的托管代码模块。 这还取决于在应用程序的 Web.config 文件的配置节中配置了哪些模块。(想要了解更多有关IIS集成模式下模块的工作,请查看官网http://www.iis.net/learn/get-started/introduction-to-iis/introduction-to-iis-architecture。)

    本节我们主要介绍了HTTP请求在进入IIS后进行的一系列模块请求,究竟由哪些模块来处理请求是由应用程序的父应用程序和Web.config配置决定。在HTTP请求进入Web Server Core后会触发一系列的管道事件,这些管道事件驱动了HTTP请求的正常运行。对于这些管道事件及IIS和.NET底层代码的运行机制,将会在下节中讲到。

    http://blog.csdn.net/zhang_xinxiu/article/details/10832805


    上节中我们从Http请求在Asp.net中的运行过程进行了分析,但是对于真正核心的东西我们并没有说明,那接下来我们将问题上抛,从底层类和对象的创建层面上来看Asp.net的运行机制。

    三、Asp.net底层运行机制

    1、理解HTTP.SYS

    HTTP.SYS:运行于Windows核心(Kernel)的一个组件,它负责侦听(Listen)来自于外部的HTTP请求(通常来自网络中另一台计算机上的浏览器),根据请求的URL将其转发给相应的应用程序池 (Application Pool)。当此HTTP请求处理完成时,它又负责将处理结果发送出去(其接收者通常为发出HTTP请求的浏览器)。为了提供更好的性能,HTTP.SYS内部建立了一个缓冲区,将最近的HTTP请求处理结果保存起来。
    通俗点说,HTTP.SYS 判断了哪些 URL 是可以访问的, 哪些是不可以访问的。 举个简单的例子,为什么你访问不存在的文件会出现 404 错误呢,就是在这一步确定的。如果请求的是一个可访问的 URL,HTTP.SYS会将这个请求交给 IIS 工作者进程。

    2、AppDomain内部运行方式

    首先来看张图,我们可以把HTTP请求画在一张图上来看。


    步骤1

    HTTP请求进入Web服务器后,首先由HTTP.SYS来判断请求的页面是否存在,如果存在的话将把请求信息转交给.NET Runtime。在这部分实际是完成两个步骤,在将请求转交给.NET Runtime的同时将请求信息封存在HTTPWorkRequest类中供其它步骤调用。
    Note:HttpWorkRequest类在以后的操作中至关重要,它第一次将Http请求信息转换为类信息。

    步骤2

    当请求到达.NET Runtime后,接下来的操作将会在托管环境中完成,这时请求就真正进入了.NET中,对请求信息的操作是由.NET的底层类库来实现。首先.NET Runtime将会针对请求信息做两个动作,一是准备HostingEnvironment;二是调用ApplicationManager类为HTTP请求动态的分配AppDomain,并把处理权交给AppDomain。

    步骤3

    HTTP请求进入AppDomain后,将由对象ISAPIRuntime来接管,一方面经方法ProcessRequest()得到HttpWorkerRequest对象,另一方面由方法StartProcessing()生成HttpRuntime对象,接下来把处理权交给了HttpRuntime(HttpWorkerRequest对象将作为HttpRuntime方法中的参数被使用)。

    步骤4

    HTTPRuntime接收到Http请求后,方法ProcessRequest处理请求。还记得步骤1中的HTTPWorkRequest类吗,此时将会针对该类中的信息进行操作,具体的实现由ProcessRequest方法实现。内部代码如下:

    1. [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Medium)]

    2. public static void ProcessRequest(HttpWorkerRequest wr)

    3. {

    4. if (wr == null)

    5. {

    6. throw new ArgumentNullException("wr");

    7. }

    8. if (UseIntegratedPipeline)

    9. {

    10. throw new PlatformNotSupportedException(System.Web.SR.GetString("Method_Not_Supported_By_Iis_Integrated_Mode", new object[] { "HttpRuntime.ProcessRequest" }));

    11. }

    12. ProcessRequestNoDemand(wr);

    13. }

    14. internal static void ProcessRequestNoDemand(HttpWorkerRequest wr)

    15. {

    16. RequestQueue queue = _theRuntime._requestQueue;

    17. if (queue != null)

    18. {

    19. wr = queue.GetRequestToExecute(wr);

    20. }

    21. if (wr != null)

    22. {

    23. CalculateWaitTimeAndUpdatePerfCounter(wr);

    24. wr.ResetStartTime();

    25. ProcessRequestNow(wr);

    26. }

    27. }

    28. internal static void ProcessRequestNow(HttpWorkerRequest wr)

    29. {

    30. _theRuntime.ProcessRequestInternal(wr);

    31. }

    [AspNetHostingPermission(SecurityAction.Demand, Level=AspNetHostingPermissionLevel.Medium)]
    public static void ProcessRequest(HttpWorkerRequest wr)
    {
    if (wr == null)
    {
    throw new ArgumentNullException("wr");
    }
    if (UseIntegratedPipeline)
    {
    throw new PlatformNotSupportedException(System.Web.SR.GetString("Method_Not_Supported_By_Iis_Integrated_Mode", new object[] { "HttpRuntime.ProcessRequest" }));
    }
    ProcessRequestNoDemand(wr);
    }
    internal static void ProcessRequestNoDemand(HttpWorkerRequest wr)
    {
    RequestQueue queue = _theRuntime._requestQueue;
    if (queue != null)
    {
    wr = queue.GetRequestToExecute(wr);
    }
    if (wr != null)
    {
    CalculateWaitTimeAndUpdatePerfCounter(wr);
    wr.ResetStartTime();
    ProcessRequestNow(wr);
    }
    }
    internal static void ProcessRequestNow(HttpWorkerRequest wr)
    {
    _theRuntime.ProcessRequestInternal(wr);
    }

    Note:在上面的请求中是不是看到有很多ProcessRequest()方法,该方法在很多类里面出现,但是所处理的请求并不相同。实际上我们可以理解为一个驱动,在处理的过程中通过该方法加工请求信息,然后驱动请求向下进行。

    步骤5

    在HttpRunTime中经过一系列的驱动后,将会在ProcessRequestInternal方法中为Http请求分配应用程序。在这一步中还将创建HttpContext对象。

    1. context = new HttpContext(wr, false); // 基于HttpWorkerRequest生成HttpContext

    2. IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context); // 得到HttpApplication

    3. handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context); // 由HttpApplication处理请求

    context = new HttpContext(wr, false); // 基于HttpWorkerRequest生成HttpContext
    IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context); // 得到HttpApplication
    handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context); // 由HttpApplication处理请求

    HttpSessionState 、HttpServerUtility等。 这些对象其实是开发人员常常使用的Asp对象,在程序中可以通过 Page 类或者 Context 类进行访问。

    步骤6

    经过步骤5后HTTP请求信息才由基本信息转交给了Asp.net中的各个对象。接下来的操作会触发一些列的管道事件,这时的请求才真正转到HttpModule和HttpHandler中。

    接下来我们看看常说的管道事件的创建过程:

    1. internal override void BuildSteps(WaitCallback stepCallback)

    2. {

    3. ArrayList steps = new ArrayList();

    4. HttpApplication app = base._application;

    5. bool flag = false;

    6. UrlMappingsSection urlMappings = RuntimeConfig.GetConfig().UrlMappings;

    7. flag = urlMappings.IsEnabled && (urlMappings.UrlMappings.Count > 0);

    8. steps.Add(new HttpApplication.ValidatePathExecutionStep(app));

    9. if (flag)

    10. {

    11. steps.Add(new HttpApplication.UrlMappingsExecutionStep(app));

    12. }

    13. app.CreateEventExecutionSteps(HttpApplication.EventBeginRequest, steps);

    14. app.CreateEventExecutionSteps(HttpApplication.EventAuthenticateRequest, steps);

    15. app.CreateEventExecutionSteps(HttpApplication.EventDefaultAuthentication, steps);

    16. app.CreateEventExecutionSteps(HttpApplication.EventPostAuthenticateRequest, steps);

    17. app.CreateEventExecutionSteps(HttpApplication.EventAuthorizeRequest, steps);

    18. app.CreateEventExecutionSteps(HttpApplication.EventPostAuthorizeRequest, steps);

    19. app.CreateEventExecutionSteps(HttpApplication.EventResolveRequestCache, steps);

    20. app.CreateEventExecutionSteps(HttpApplication.EventPostResolveRequestCache, steps);

    21. steps.Add(new HttpApplication.MapHandlerExecutionStep(app));

    22. app.CreateEventExecutionSteps(HttpApplication.EventPostMapRequestHandler, steps);

    23. app.CreateEventExecutionSteps(HttpApplication.EventAcquireRequestState, steps);

    24. app.CreateEventExecutionSteps(HttpApplication.EventPostAcquireRequestState, steps);

    25. app.CreateEventExecutionSteps(HttpApplication.EventPreRequestHandlerExecute, steps);

    26. steps.Add(new HttpApplication.CallHandlerExecutionStep(app));

    27. app.CreateEventExecutionSteps(HttpApplication.EventPostRequestHandlerExecute, steps);

    28. app.CreateEventExecutionSteps(HttpApplication.EventReleaseRequestState, steps);

    29. app.CreateEventExecutionSteps(HttpApplication.EventPostReleaseRequestState, steps);

    30. steps.Add(new HttpApplication.CallFilterExecutionStep(app));

    31. app.CreateEventExecutionSteps(HttpApplication.EventUpdateRequestCache, steps);

    32. app.CreateEventExecutionSteps(HttpApplication.EventPostUpdateRequestCache, steps);

    33. this._endRequestStepIndex = steps.Count;

    34. app.CreateEventExecutionSteps(HttpApplication.EventEndRequest, steps);

    35. steps.Add(new HttpApplication.NoopExecutionStep());

    36. this._execSteps = new HttpApplication.IExecutionStep[steps.Count];

    37. steps.CopyTo(this._execSteps);

    38. this._resumeStepsWaitCallback = stepCallback;

    39. }

    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.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(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;
    }

    具体的管道请求事件步骤如下图:

    四、Asp对象的获取原则

    Application对象

    为当前 Web 请求获取 HttpApplicationState 对象

    1. public HttpApplicationState Application { get; }

    public HttpApplicationState Application { get; }

    Request对象

    获取请求的页的 HttpRequest 对象。

    1. public HttpRequest Request { get; }

    public HttpRequest Request { get; }

    Response对象

    获取与该 Page 对象关联的 HttpResponse 对象。 该对象使您得以将 HTTP 响应数据发送到客户端,并包含有关该响应的信息。

    1. public HttpResponse Response { get; }

    public HttpResponse Response { get; }

    Session对象

    获取 ASP.NET 提供的当前 Session 对象。
    该属性提供有关当前请求的会话的信息。 为从 ASP.NET 应用程序请求页或文档的每个用户维护一个 Session 对象。 当用户在应用程序中从一页移动到另一页时,存储在 Session 对象中的变量不会被放弃;相反,只要用户在应用程序中访问页,这些变量就将保持。

    1. public virtual HttpSessionState Session { get; }

    public virtual HttpSessionState Session { get; }

    五、回顾

    说了这么多其实都是由Asp.net运行机制引发的。刚开始我们从IIS架构着说,着重介绍了IIS内部的一些结构,然后围绕该服务器管理程序展开了一系列的论述,这些论述虽然很多都是理论知识,但对于我们深入学习Web应用程序来说却有着十分重要的作用,帮助开发人员更好的开发Web应用程序打下基础。

    学习有粗有细,对于一些基本的概念或者一些整体的知识点我们要进行粗略的理解,但对于在实际开发过程中要应用到的知识我们要进行细致的掌握,这样在应用时才不至于出错,做到游刃有余。这也可以说是学习的一种全局观,对知识点有整体、粗略的把握,才能更细致的运用知识,理论加实践这才是学技术的最高境界。

    Asp.net和IIS内部运行交互过程我们已经讲完,接下来将会着重从Web应用程序角度入手讲解开发人员所需要的Asp.net知识。

    http://blog.csdn.net/zhang_xinxiu/article/details/10851417

    (责任编辑:佚名


    上一篇:Win2003解决事件ID(487)

    下一篇:深入浅出解读IIS架构