通常咱们都在写业务代码,优化页面,优化逻辑之间内徘徊。也许咱们懂得HTTP,HTTPS的GET,POST,可是咱们大部分人是不知道ASP是如何去解析HTTP,或者IIS是如何去处理页面请求。咱们只知道WebForm拉控件,MVC写Controller,Action,殊不知道IIS,NetFrameWork帮咱们作了不少事情。那接下咱们就是要去了解IIS帮咱们作了些啥事情。php
初理解Pipelinehtml
从一个现象提及,有一家咖啡吧生意特别好,天天来的客人络绎不绝,客人A来到柜台,客人B紧随其后,客人C排在客人B后面,客人D排在客人C后面,客人E排在客人D后面,一直排到店面门外。老板和三个员工首先为客人A准备食物:员工甲拿了一个干净的盘子,而后员工乙在盘子里装上薯条,员工丙再在盘子里放上豌豆,老板最后配上一杯饮料,完成对客人A的服务,送走客人A,下一位客人B开始被服务。而后员工甲又拿了一个干净的盘子,员工乙又装薯条,员工丙又放豌豆,老板又配上了一杯饮料,送走客人B,客人C开始被服务。一直重复下去。git
从效率方面观察这个现象,当服务客人A时,在员工甲拿了一个盘子后,员工甲一直处于空闲状态,直到送走客人A,客人B被服务。老板天然而然的就会想到若是每一个人都不停的干活,就能够服务更多的客人,赚到更多的钱。老板经过不停的尝试想出了一个办法。以客户A,B为例阐述这个方法:员工甲为客户A准备好了盘子后,在员工乙开始为客户A装薯条的同时,员工甲开始为客户B准备托盘。这样员工甲就能够不停的进行生产。整个过程以下图,客户们围着咖啡吧台排队,由于有四个生产者,一个老板加三个员工,因此能够同时服务四个客户。咱们将目光转向老板,单位时间从他那里出去的客户数提升了将近四倍,也就是说效率提升将近四倍。github
这样子,咱们就很好理解了Pipeline了,那其实就是每一个人络绎不绝的作本身的事情,但我的事情又是整个流程的一部分,有点像工厂的小妹,只作包鞋底,可是这又是制造鞋的中间流程。一条流水,一条管道,一直处理下去。那每一个HTTP请求,到了IIS也是这样子的。web
Pipeline模型的缺点
每次它对于一个输入(或者一次请求)都必须从链头开始遍历(參考Http Server处理请求就能明确),这确实存在必定的性能损耗。json
当服务器接收到一个Http请求的时候,IIS是如何去决定,并处理该请求。答案就是文件的“后缀名”。api
服务器获取所请求的页面或文件的后缀名后,那服务器就回去寻找能出来这些后缀的应用程序。如果IIS找不到,而且文件没有受到IIS的保护(保护:App_Code文件夹的文件,不保护:日常的JS文件等),就会直接返回给客户端(浏览器,移动端等)浏览器
那么能处理后缀名的程序叫什么呢? ISAPI 应用程序(Internet Server Application Programe Interface,互联网服务器应用程序接口)。它实际上是一个接口,起到一个代理的做用,他的主要工做就是将请求的页面(文件)与处理该页面(文件)的后缀的处理程序进行一个映射。让其能够争取的去处理页面(文件)。服务器
那这个应用程序长什么样子呢?这边咱们能看到后缀名与之可执行的文件路径。
接着咱们找到“.axpx”的后缀名,打开来看。mvc
咱们能够发现".aspx"的后缀名是有aspnet_isapi.dll来处理的,因此IIS将".aspx"页面的请求交给这个dll,就不关心后面是如何处理了。因此咱们如今知道了,ASP.NET只是服务器(IIS)的一个组成部分而已,它是一个ISAPI扩展。
上面是server2003的状况,如今08以上变成了不是在属性里面了, 他变成了IIS中的“ISAPI筛选器”和“处理程序映射”了。
3.双击进入,能够看到能够处处理模块的具体路径,以及请求限制。
注意,这边要是限制后,页面(文件)只能以某种特定方式访问。
从本质上讲,Asp.Net 主要是由一系列的类组成,这些类的主要目的就是将 Http 请求转变为对客户端的响应。HttpRuntime 类是 Asp.Net 的一个主要入口,它有一个称做 ProcessRequest的方法,这个方法以一个 HttpWorkerRequest 类做为参数。HttpRuntime 类几乎包含着关于单个 Http 请求的全部信息:所请求的文件、服务器端变量、QueryString、Http 头信息 等等。Asp.Net 使用这些信息来加载、运行正确的文件,而且将这个请求转换到输出流中,通常来讲,也就是 HTML 页面。固然也但是是文件。
当页面(文件)发生变更时,为了可以卸载运行在同一进程中的应用程序,固然卸载也是为了从新新加载,Http请求被分放在相互隔离的应用程序域。也就是“AppDomain”。
那么对于IIS来讲,IIS依赖一个叫HTTP.SYS的内置驱动程序来监听外部的HTTP请求,在操做系统启动的时候,IIS会在HTTP.SYS中注册本身的虚拟路径。(注册能够理解为,告诉HTTP.SYS哪些URL能够访问,哪些不能够访问,404就能够怎么来的,就是这么来的。)
若是是能够访问的URL,那么HTTP.SYS会将这个请求扔给IIS工做者进程。(就是咱们所熟悉的W3WP.EXE,IIS5.0叫作ASPNET_WP.EXE)。
运行是每一个进程都有一个身份标识,以及一系列的可选性能参数,其实就是应用程序池,惟一标识就是应用程序池名称。
基础的知识点都懂了,咱们来看下一个大概的HTTP请求的过程
前面讲了那么多,终于进入到重点,划重点。
首先咱们先知道了什么是Pipeline,接着咱们知道一个Http的请求过程。而后咱们就知道了,原来Http请求到服务器,原来是走这样一条路。
可是咱们忽略了,这个过程跟咱们程序,跟咱们代码怎么衔接起来的。
当Http请求进入 Asp.Net Runtime之后,它的管道由托管模块(Managed Modules)和处理程序(Handlers)组成,而且由管道来处理这个 Http请求。这个就是所谓的ASP的Http管道模型了。
接下来咱们看下这个管道模型是怎么流动的。(WebForm)
当Http请求完,它会被HttpHandler处理。在这一步,也就是咱们实际作的事情了,他能够完成咱们.aspx的全部业务。WebForm的每一个页面都是继承“Page”,而“Page”继承了“IHttpHandler”接口
public class Page : TemplateControl, IHttpHandler{ // 代码省略 }
咱们能够看下,在只有Module与Handler的状况,整个Http的流动走向,大概是这样子
在IIS7以前,如IIS6或IIS5,请求处理管道分为两个:IIS请求处理管道和ASP.NET管道,若客户端请求静态资源则只有IIS管道进行处理,而ASP.NET管道不会处理该请求。从IIS7开始两个管道合二为一,称为集成管道。其实就是咱们IIS里会选择到应用程序池的托管模式。
再次了解
IIS 6以及IIS 7经典模式
早期的IIS版本中,IIS接收到一个请求时先判断请求类型,若是是静态文件直接由IIS处理;若是是一个ASP.NET请求类型,IIS把请求发送给IIS的扩展接口ASP.NET ISAPI DLL。ISAPI至关于ASP.NET应用的容器,这也是咱们以前讲的。
IIS 7 集成模式
IIS 7和以前的版本区别比较大,IIS7直接把ASP.NET的运行管道流程集成到了IIS上。
知道这些咱们继续了解MVC的管道模式
废话很少说,直接上图
废话也很少说,直接走下流程(以个人方式理解)
废话有点多
接下来咱们具体了解下细节
HttpApplication与HttpModule
HTTP请求由ASP.NET运行时接管以后,HttpRuntime会利用HttpApplicationFactory建立或从HttpApplication对象池(.NET中相似的机制有线程池和字符串拘留池)中取出一个HttpApplication对象,同时ASP.NET会根据配置文件来初始化注册的HttpModule,HttpModule在初始化时会订阅HttpApplication中的事件来实现对HTTP请求的处理。
public class HttpApplication : IComponent, IDisposable, IHttpAsyncHandler, IHttpHandler, IRequestCompletedNotifier, ISyncContext{ public HttpApplication(); public ISite Site { get; set; } public IPrincipal User { get; } ... }
很明显咱们能够看到HttpApplication继承了IHttpHandler,是跟WebForm的Page是同样的
Route
一个HTTP请求会通过至少一个HttpModule的处理。UrlRoutingModule是很是重要的模块,它是路由系统的核心。路由系统的职责是从请求URL中获取controller和action的名称以及其它请求数据。
UrlRoutingModule根据当前请求的URL和RouteTable中已注册的路由模板进行匹配并返回第一个和当前请求相匹配的路有对象Route,而后根据路有对象获取路由数据对象RouteData(ASP.NET MVC中,路由数据必须包含controller和action的名称),再有RouteData获取IRouteHandler最终有IRouteHandler获得IHttpHandler
HttpHandler
一个HTTP请求最终要进入HttpHanler中进行处理,一次HTTP请求只能被一个HttpHandler进行处理。
Controller
IHttpHandler在ProcessRequest方法中对当前请求进行处理,在该方法中经过ControllerBuilder获得IControllerFactory而后经过反射的方式获取Controller的类型。
Action
ASP.NET MVC中ControllerBase是全部Controller的基类,在该类型的Execute方法中经过IActionInvoker的InvokeAction方法来执行对Action的调用。在Action执行前会进行模型绑定和模型认证操做。
Filters
在ASP.NET MVC5中有经常使用的过滤器有5个:IAuthenticationFilter、IAuthorizationFilter、IActionFilter、IResultFilter、IExceptionFilter。
在ASP.NET MVC中全部的过滤器最终都会被封装为Filter对象,该对象中FilterScope类型的属性Scope和int类型属性Order用于决定过滤器执行的前后顺序,具体规则以下:
Order和FilterScope的数值越小,过滤器的执行优先级越高;
Order比FilterScope具备更高的优先级,在Order属性值相同时FilterScope才会被考虑
//数值越小,执行优先级越高 public enum FilterScope { Action= 30, Controller= 20, First= 0, Global= 10, Last= 100 }
ActionResult
Action执行完毕以后会返回ActionResult类型对象做为对这次请求进行处理的结果,对于不是ActionResult类型的返回值,ASP.NET MVC会将其转换为ActionResult类型。
总结:那这个就是MVC模式下,一个大概的请求走向,在MVC的模式下,它的管道是啥样子的。
既然Core是跨平台的,那么它不依托IIS,如今的IIS就是个摆设,它有独立的Core的SDK,Core的RunTime。咱们来一探究竟!
Core是跨平台的,那他是怎么实现的呢,其实他本身自己有本身的Kestrel Server,能够直接对外部提供服务。可是还须要有个反向代理服务器将他保护起来,IIS就是其一,其余平台有其余的方式。
上图:
知识点:IIS 是经过 HTTP 的方式来调用咱们的 ASP.NET Core 程序。而部署在IIS中时,并不须要咱们手动来启动 ASP.NET Core 的控制台程序,这是由于IIS新增了一个 AspNetCoreModule 模块,它负责 ASP.NET Core 程序的启动与中止,并能监听 ASP.NET Core 程序的状态,在咱们的应用程序意外崩溃时从新启动。
Hostting(宿主)
IIS经过Http调用Core应用程序,因此咱们确定要建立一个Host来启动Core,WebHost
,上面咱们也讲到宿主,咱们经过宿主在生成一系列的Http的上下文,环境等。整个http请求都在宿主内。
WebHost的建立
public class Program{
public static void Main(string[] args){
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>(); }
咱们能够看到WeHost经过静态自己来调用建立出来的。“CreateDefaultBuilder”用来作一些简单的配置
- 注册 Kestrel 中间件,指定 WebHost 要使用的 Server(HTTP服务器)。
- 设置 Content 根目录,将当前项目的根目录做为 ContentRoot 的目录。
- 读取 appsettinggs.json 配置文件,开发环境下的 UserSecrets 以及环境变量和命令行参数。
- 读取配置文件中的 Logging 节点,对日志系统进行配置。
- 添加 IISIntegration 中间件。
- 设置开发环境下, ServiceProvider 的 ValidateScopes 为 true,避免直接在 Configure 方法中获取 Scope 实例。
接着指定Startup
最终使用Build
建立WebHost。紧接着使用Run
将程序跑起来。
public delegate Task RequestDelegate(HttpContext context);
Func<RequestDelegate, RequestDelegate>
类型的委托(也就是中间件)来实现的。它接收一个 RequestDelegate 类型的参数,并返回一个 RequestDelegate 类型,也就是说前一个中间件的输出会成为下一个中间件的输入,这样把他们串联起来,造成了一个完整的管道。Use
是咱们很是熟悉的注册中间件的方法,其实现很是简单,就是将注册的中间件保存到其内部属性 _components 中。Hosting
的启动中,即是经过该 Build 方法建立一个 RequestDelegate 类型的委托,Http Server 经过该委托来完成整个请求的响应New
方法根据自身来“克隆”了一个新的 ApplicationBuilder 对象,而新的 ApplicationBuilder 能够访问到建立它的对象的 Properties
属性,可是对自身 Properties 属性的修改,却不到影响到它的建立者,这是经过 CopyOnWriteDictionary 来实现的因此 Core的管道其实就是Middleware来作的,每一个都有前置,后置的处理步骤,中间能够调用其余Middleware。也能够并行走向。
ASP.NET Core 请求管道的构建过程,以及一些帮助咱们更加方便的来配置请求管道的扩展方法。在 ASP.NET Core 中,至少要有一个中间件来响应请求,而咱们的应用程序实际上只是中间件的集合,MVC 也只是其中的一个中间件而已。简单来讲,中间件就是一个处理http请求和响应的组件,多个中间件构成了请求处理管道,每一个中间件均可以选择处理结束,仍是继续传递给管道中的下一个中间件,以此串联造成请求管道。一般,咱们注册的每一个中间件,每次请求和响应均会被调用,但也可使用 Map
, MapWhen
,UseWhen
等扩展方法对中间件进行过滤。
那咱们分析了以往微软的3种形式底下的Web,分别是WebForm,MVC,Core,其中WebForm与MVC雷士,依托于IIS,不一样的就是ISAPI
的不一样。前者的ISAPI以插件形式存在IIS,然后者将其整合成一套。Core跨平台,IIS只是他的一个反向代理服务器,HTTP请求带Core的程序,运行Core。
可是我以为他们内容是基本不变的,HTTP请求,进来在彼此的AppDomain
中建立HttpContext
,而后就开始走Pipeline
走流程,WebFrom走Page
的IHTTPHandler
与其相关的IModule,而MVC是Global.asax
继承HttpApplication
,HttpApplication
又继承IHTTPHandler
,接下来WebFrom与MVC就各自走各自的Pipeline
。Core却不是这样,它的Pipeline
采用了Middleware
(中间件)的形式。俄罗斯套娃,一步一步走Pipeline
。最终都是响应客户端,一个HTTP请求,从开始到结束。