zoukankan      html  css  js  c++  java
  • HTTP协议/IIS 原理及ASP.NET运行机制浅析【图解】

    转自:http://www.uml.org.cn/net/201306193.asp

    前言

    前一段在整理邮件的时候发现几年前和CDD老师交流时的一份邮件.下面是简单摘要:

    “从技术角度来说,无论哪一个阵营,跟新技术都是不可避免的,也是很累的,当然作为一个程序员来说,也是必须的。要想让技术的更新对自己的影响减小,基础就必须打牢。所以,底层的东西和抽象层的东西需要下一番功夫。因为说到底,无论什么技术,无非就是架构和最终的实现,技术框架只是应用开发的一个平台一种技术,如果了解了具体的东西,技术更新对你来说就没什么影响了,或者换句话说,你要学一种新的技术,速度和效率会非常之高。”

    上面一段话对自己的影响很大,可能大家在踏入“程序人生”的时候都会存在一些迷茫和彷徨。尽管我是属于那种相当热爱Proramming的一份子,但是面对万花筒般的技术分支也曾徘徊犹豫过.徘徊之余要做的事情便是夯实基础,寻找自己的兴趣与方向.对技术的迭代,以不变应万变才是王道.

    正因为如此,所以也不会存在银弹之说.如果真的有银弹的话那么我信奉的是:程序=数据结构+算法

    我选择的方向是Web,也相信Web终究会是互联网的未来.这篇文章简单谈一下自己对.NET平台下Web基础的一些浅解,由于自己水平有限,不足之处烦请见谅.

    HTTP协议

    HTTP协议是浏览器和服务器双方共同遵循的规范.是一种基于TCP/IP(传输层协议,相对应的有UDP)的"应用层协议"

    PS:TCP/UDP是广泛使用的网络通信协议,UDP协议具有不可靠性和不安全性

    相对来说TCP协议是基于连接和三次握手的(相对可靠与安全),然而B/S架构的网站,由于同时在线的人数会很多,如果都与服务器保持连接状态.服务器的承载是相当大的,

    因而衍生出HTTP协议.简单的说:请求发起之后服务器端立刻关闭连接并释放资源.也正因为如此,HTTP协议通常被理解为”无状态”的.

    当然维系"状态"的手段有很多;如 Session/Cookie等 这里暂且不多做讨论.

    先来看一下典型的OSI七层模型 图解

    HTTP最通俗的理解 请求/响应.

    图示:

    HTTP报文信息

    HTTP Request Header

    HTTP Response Heade

    当然,也可以通过设置改变浏览器的选项.这里不做详细说明.不清楚的可以Google.

    给出ASP.NET下添加P3P头信息的例子

    HttpContext.Current.Response.AddHeader("p3p", "CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"");

    有兴趣详细了解的可以参考 MSDN 中关于部署 P3P的文章。

    下面是老生常谈的内容了(熟悉的朋友,自行跳过,权当温习下了 : ) )

    请求头(消息头)包含(客户机请求的服务器主机名,客户机的环境信息等):

    1、Accept:用于告诉服务器,客户机支持的数据类型 (例如:Accept:text/html,image/*)

    2、Accept-Charset:用于告诉服务器,客户机采用的编码格式

    3、Accept-Encoding:用于告诉服务器,客户机支持的数据压缩格式

    4、Accept-Language:客户机语言环境

    5、Host:客户机通过这个服务器,想访问的主机名

    6、If-Modified-Since:客户机通过这个头告诉服务器,资源的缓存时间

    7、Referer:客户机通过这个头告诉服务器,它(客户端)是从哪个资源来访问服务器的(防盗链)

    8、User-Agent:客户机通过这个头告诉服务器,客户机的软件环境(操作系统,浏览器版本等)

    9、Cookie:客户机通过这个头,将Coockie信息带给服务器

    10、Connection:告诉服务器,请求完成后,是否保持连接

    11、Date:告诉服务器,当前请求的时间

    一个http响应代表服务器端向客户端回送的数据,它包括:

    一个状态行,若干个响应消息头,以及实体内容

    状态行: 例如: HTTP/1.1 200 OK (协议的版本号是1.1 响应状态码为200 响应结果为 OK)

    响应头(消息头)包含:

    1、Location:这个头配合302状态吗,用于告诉客户端找谁

    2、Server:服务器通过这个头,告诉浏览器服务器的类型

    3、Content-Encoding:告诉浏览器,服务器的数据压缩格式

    4、Content-Length:告诉浏览器,回送数据的长度

    5、Content-Type:告诉浏览器,回送数据的类型

    6、Last-Modified:告诉浏览器当前资源缓存时间

    7、Refresh:告诉浏览器,隔多长时间刷新

    8、Content- Disposition:告诉浏览器以下载的方式打开数据。例如:

    context.Response.AddHeader("Content-Disposition","attachment:filename=icon.jpg");

    context.Response.WriteFile("icon.jpg");

    9、Transfer-Encoding:告诉浏览器,传送数据的编码格式

    10、ETag:缓存相关的头(可以做到实时更新)

    11、Expries:告诉浏览器回送的资源缓存多长时间。如果是-1或者0,表示不缓存

    12、Cache-Control:控制浏览器不要缓存数据 no-cache

    13、Pragma:控制浏览器不要缓存数据 no-cache

    14、Connection:响应完成后,是否断开连接。 close/Keep-Alive

    15、Date:告诉浏览器,服务器响应时间

    IIS运行过程

    有了上面的HTTP协议的知识回顾,下面来让我们看下IIS是怎样工作的?

    IIS 5.X 已经距离我们很远了.好吧 XP默认的好像是的… 为万恶的IE6 默哀下0.0 .

    这里我们来看一下IIS 6 的图示

    根据上图简单分析下IIS6的运行过程

    在 User Mode 下,http.sys 接收到 http request,然后它会根据 IIS 中的 Metabase 查看基于该 Request 的 Application 属于哪个 Application Pool, 如果该 Application Pool 不存在,则创建之。否则直接将 request 发到对应 Application Pool 的 Queue中。

    每个 Application Pool 对应着一个 Worker Process — w3wp.exe,(运行在 User Mode 下)。在 IIS Metabase 中维护着 Application Pool 和 Worker Process 的Mapping。WAS(Web Administrative Service)根据这样一个 mapping,将存在于某个 Application Pool Queue 的 request 传递到对应的 Worker Process (如果没有,就创建这样一个进程)。在 Worker Process 初始化的时候,加载 ASP.NET ISAPI,ASP.NET ISAPI 进而加载 CLR。最后通过 AppManagerAppDomainFactory 的 Create 方法为 Application 创建一个 Application Domain;通过 ISAPIRuntime 的 ProcessRequest 处理 Request,进而将流程进入到 ASP.NET Http Runtime Pipeline。

    PS几个知识点:

    1、HTTP.SYS:(Kernel)的一个组件,它负责侦听(Listen)来自于外部的HTTP请求,根据请求的URL将其转发给相应的应用程序池 (Application Pool)。当此HTTP请求处理完成时,它又负责将处理结果发送出去.为了提供更好的性能,HTTP.SYS内部建立了一个缓冲区,将最近的HTTP请求处理结果保存起来。

    2、Application Pool: IIS总会保持一个单独的工作进程:应用程序池。所有的处理都发生在这个进程里,包括ISAPI dll的执行。对于IIS6而言,应用程序池是一个重大的改进,因为它们允许以更小的粒度控制一个指定进程的执行。你可以为每一个虚拟目录或者整个Web 站点配置应用程序池,这可以使你很容易的把每一个应用程序隔离到各自的进程里,这样就可以把它与运行在同一台机器上其他程序完全隔离。从Web处理的角度看,如果一个进程死掉,至少它不会影响到其它的进程。

    当应用程序池接收到HTTP请求后,交由在此应用程序池中运行的工作者进程Worker Process: w3wp.exe来处理此HTTP请求。

    3、Worker Process: 当工作者进程接收到请求后,首先根据后缀找到并加载对应的ISAPI扩展 (如:aspx 对应的映射是aspnet_isapi.dll),工作者进程加载完aspnet_isapi.dll后,由aspnet_isapi.dll负责加载 ASP.NET应用程序的运行环境即CLR (.NET Runtime)。

    Worker Process运行在非托管环境,而.NET中的对象则运行在托管环境之上(CLR),它们之间的桥梁就是ISAPI扩展。

    4、WAS(Web Admin Service):这是一个监控程序,它一方面可以存取放在InetInfo元数据库(Metabase)中的各种信息,另一方面也负责监控应用程序池(Application Pool)中的工作者进程的工作状态况,必要时它会关闭一个老的工作者进程并创建一个新的取而代之。

    再来看下网上对IIS7经典模式下的图解

    IIS 7 应用程序池的托管管道模式“经典”模式也是这样的工作原理。这种模式是兼容 IIS 6 的方式, 以减少升级的成本。

    小插曲:场景假定:

    截获客户端的请求,并对请求进行重写。在IIS6中,请求的截获动作只能被限制在IIS加载aspnet_isapi.dll后,也就是说:如果该请求不是明确针对asp.net资源的请求(比如这个请求只是一个静态文件的请求,如www.cnblogs.com/index.html,这时我们就便不能在代码中编写截获请求的逻辑,因为IIS6是根据URL的后缀来映射并加载对应的isapi的,如果一个请求的url 是:www.cnblogs.com/index.aspx,根据".aspx"这个后缀,IIS6可以得知这个请求是针对asp.net资源的,应该加载aspnet_isapi.dll创建.net运行时并运行asp.net页面的代码,但很明显,诸如"www.cnblogs.com/index.html"这种请求,IIS6通常认为不是对asp.net资源的请求,因此不会加载 aspnet_isapi.dll来运行asp.net,我们即使在asp.net页面中编写了拦截请求的代码,也不会被执行。当然,这里我说通常是有原因的,因为我们可以在IIS6中添加通配符程序映射的方式,或者在web.config中对某种请求手动添加处理程序的方式,来迫使IIS6为非 asp.net资源类型的请求加载aspnet_isapi.dll。IIS6中对请求的执行流程如上.

    咦,有木有人和我一样想到了URL Routing 和URL Rewriting ?

    这里不做说明,大叔手记16传送门:http://www.cnblogs.com/TomXu/archive/2011/12/27/2303486.html

    这个问题先放一下~~了解II7的集成模式也许可以有一些思绪

    让我们再来看下IIS官网上对IIS7的图解

    传送门 :http://www.iis.net/learn/get-started/introduction-to-iis/introduction-to-iis-architecture

    1、当客户端浏览器开始 HTTP 请求一个WEB 服务器的资源时,HTTP.sys 拦截到这个请求。

    2、HTTP.sys 联系 WAS 获取配置信息。

    3、WAS 向配置存储中心(applicationHost.config)请求配置信息。

    4、WWW 服务接收到配置信息,配置信息指类似应用程序池配置信息,站点配置信息等等。

    5、WWW 服务使用配置信息去配置 HTTP.sys 处理策略。

    6、WAS为请求创建一个进程(如果不存在的话)

    7、工作者进程处理请求并对HTTP.sys做出响应.

    8、客户端接受到处理结果信息。

    IIS 7 应用程序池的托管管道模式(集成模式)华丽的变身

    IIS7中对asp.net的请求不再是分两条处理管道,而是将asp.net和 IIS集成起来,这样做的好处是统一了请求验证工作,加强了asp.net对于请求的控制能力等等。在IIS7中,asp.net不再像IIS6一样只限定于aspnet_isapi.dll中,而是被解放出来,从IIS接收到HTTP请求开始,即进入asp.net的控制范围,asp.net可以存在于一个请求在IIS中各个处理阶段。甚至可以为部署在IIS7中的PHP应用提供基于asp.net的验证身份验证功能(传送门:http://msdn.microsoft.com/zh-cn/magazine/cc135973.aspx)。

    好吧,戛然而止一下,篇幅有限:IIS部分告一段落 留一些遐想空间.

    再来分析ASP.NET的运行机制

    ASP.NET运行机制

    在IIS6图示中我们分析到“ AppManagerAppDomainFactory 的 Create 方法为 Application 创建一个 Application Domain;通过 ISAPIRuntime 的 ProcessRequest 处理 Request,进而将流程进入到 ASP.NET Http Runtime Pipeline。”

    下面我们来看一下AppDomain运行过程图示

    AppDomain的作用,相信大家都很了解了吧.这里简明扼要的写几点:

    1、一个AppDomain中的代码创建的对象不能由另一个AppDomain中的代码直接访问(只能使用按引用封送或者按值封送,起到了很好的隔离作用).

    2、AppDomain可以卸载 CLR不支持从AppDomain中卸载一个程序集的能力,但可以告诉CLR卸载一个AppDomain,从而达到卸载当前包含在该AppDomain内的所有程序集.

    3、AppDomain 可以单独保护 当宿主加载一些代码之后,可以保证这些代码不会被破坏(或读取)宿主本身使用的一些重要的数据结构.

    4、AppDomain可以单独配置 设置主要影响CLR在AppDomain中加载程序集的方式,涉及搜索路径、版本绑定重定向、卷影复制及加载器的优化。

    由以上几点可以看出AppDomain确保了Windows系统及其中运行的应用程序的健壮性。AppDomain提供了保护、配置和终止其中每一个应用程序所需的隔离性。

    再来看下ProcessRequest的过程

    简单分析一下上图

    ProcessRequest(HttpWorkerRequest wr)中判断wr是否为null,然后判断管线是否完整,再调用ProcessRequestNoDemand(wr)方法,

    并判断当前RequestQueue 是否为null,接着计算等待时间并更新管线数 CalculateWaitTimeAndUpdatePerfCounter(wr);

    重置wr开始时间wr.ResetStartTime();调用ProcessRequestNow(wr)方法,并调用ProcessRequestInternal(wr)方法

    继续图例

    ProcessRequestInternal方法如下:

    private void ProcessRequestInternal(HttpWorkerRequest wr)   
      {   
          HttpContext context;   
          try   
          {   
              context = new HttpContext(wr, false);//由HttpWorkerRequest生成HttpContext   
          }   
          catch   
          {  
        
            //常见的400错误,就是在这里捕捉到滴    
              wr.SendStatus(400, "Bad Request");   
              wr.SendKnownResponseHeader(12, "text/html; charset=utf-8");   
              byte[] bytes = Encoding.ASCII.GetBytes("<html><body>Bad Request</body></html>");   
              wr.SendResponseFromMemory(bytes, bytes.Length);   
              wr.FlushResponse(true);   
              wr.EndOfRequest();   
              return;   
          }   
          wr.SetEndOfSendNotification(this._asyncEndOfSendCallback, context);   
          Interlocked.Increment(ref this._activeRequestCount);   
          HostingEnvironment.IncrementBusyCount();   
          try   
          {   
              try   
              {   
                  this.EnsureFirstRequestInit(context);   
              }   
              catch   
              {   
                  if (!context.Request.IsDebuggingRequest)   
                  {   
                      throw;   
                  }   
              }   
              context.Response.InitResponseWriter();   
              IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance(context);   
      //得到HttpApplication        
        
      if (applicationInstance == null)   
              {   
                  throw new HttpException(System.Web.SR.GetString("Unable_create_app_object"));   
              }   
              if (EtwTrace.IsTraceEnabled(5, 1))   
              {   
                  EtwTrace.Trace(EtwTraceType.ETW_TYPE_START_HANDLER,
     context.WorkerRequest, applicationInstance.GetType().FullName, "Start");   
              }   
              if (applicationInstance is IHttpAsyncHandler)   
              {   
                  IHttpAsyncHandler handler2 = (IHttpAsyncHandler) applicationInstance;   
                  context.AsyncAppHandler = handler2;   
                  handler2.BeginProcessRequest(context, this._handlerCompletionCallback, context);//届时 HttpApplication处理请求   
              }   
              else   
              {   
                  applicationInstance.ProcessRequest(context);   
                  this.FinishRequest(context.WorkerRequest, context, null);   
              }   
          }   
          catch (Exception exception)   
          {   
              context.Response.InitResponseWriter();   
              this.FinishRequest(wr, context, exception);   
          }   
      }

    再看下GetApplicationInstance(context) 实例化Application的方法

    View Code   
     internal static IHttpHandler GetApplicationInstance(HttpContext context)  
     {  
         if (_customApplication != null)  
         {  
             return _customApplication;  
         }  
         if (context.Request.IsDebuggingRequest)  
         {  
             return new HttpDebugHandler();  
         }  
         _theApplicationFactory.EnsureInited();  
         _theApplicationFactory.EnsureAppStartCalled(context);  
         return _theApplicationFactory.GetNormalApplicationInstance(context);  
     } 

    最后调用的GetNormalApplicationInstance方法中对当前空闲的application数目进行判断,调用application.InitInternal(context, this._state, this._eventHandlerMethods)方法,this.InitModules()初始化所有的Modules,包含用户自定义的HttpModules this._stepManager.BuildSteps(this._resumeStepsWaitCallback);//管道事件序列

    贴一下源码:

    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.NET管道机制能够有一个简单的回顾.当然还有很多地方没有详细分析。

    再来总结一下IIS运行过程及ASP.NET管道机制:

    Request→ (Internet ) HTTP.sys 监听 → WAS (IIS6 web Admin Service /IIS7 (Windows Activation Service) 接收请求

    →(传入)Application Pool's → w3wp.exe(检查URL后缀)

    →(加载)ISAPI扩展[aspnet_isapi.dll] → 注册映射

    构造HttpRuntime类 →ProcessRequest方法

    HttpContext实例产生(Request,Response,Session and so on…)

    HttpRuntime 调用 HttpApplicationFactory加载HttpApplication对象

    穿越HttpModule到达HttpHandler

    简单用140个字符(即一条微博的字数)概括:

    Request→ (Internet ) HTTP.sys →(WAS)→Application Pool's → w3wp.exe→ISAPI→ Map→ (Pipeline)HttpWorkerRequest→AppDomain→HttpRuntime→ProcessRequest()→ HttpContext(Request,Response)→ HttpRuntime→HttpApplicationFactory→HttpApplication→ HttpModule→HttpHandler→EndRequest

    以上为个人学习摘要,如有错误,欢迎指正!!

    补充

    1:刚刚看到dudu发的一个闪存,里面提到了Application pool 与 AppDomain 的区别 来自stackoverflow,希望对大家有所帮助.

    2:WAS缩写在IIS6中的指的是(Web Admin Service),在IIS7中指的是(Windows Activation Service) 缩写一样.这个在写文章的时候注意到过,但是没有深入考虑. 理解不是很到位. 暂不妄下断言. 欢迎斧正!! :-)

    参考资料:

    1、http://www.cnblogs.com/tenghoo/archive/2009/11/04/IIS_And_ASPNET_Http_Runtime_Pipeline.html

    2、http://msdn.microsoft.com/en-us/library/system.web.httpapplication(v=vs.80).aspx

    3、http://www.cnblogs.com/panchunting/archive/2013/04/11/ASPNET_Architecture.html

     

  • 相关阅读:
    php环境配置中各个模块在网站建设中的功能
    PHP+Apache+MySQL+phpMyAdmin在win7系统下的环境配置
    August 17th 2017 Week 33rd Thursday
    August 16th 2017 Week 33rd Wednesday
    August 15th 2017 Week 33rd Tuesday
    August 14th 2017 Week 33rd Monday
    August 13th 2017 Week 33rd Sunday
    August 12th 2017 Week 32nd Saturday
    August 11th 2017 Week 32nd Friday
    August 10th 2017 Week 32nd Thursday
  • 原文地址:https://www.cnblogs.com/xuedognqing/p/11315635.html
Copyright © 2011-2022 走看看