颜面部除皱,2011存款利率表,灵狐者裙底h吧
一般我们都在写业务代码,优化页面,优化逻辑之间内徘徊。也许我们懂得http,https的get,post,但是我们大部分人是不知道asp是如何去解析http,或者iis是如何去处理页面请求。我们只知道webform拉控件,mvc写controller,action,却不知道iis,netframework帮我们做了很多事情。那接下我们就是要去了解iis帮我们做了些啥事情。
初理解pipeline
从一个现象说起,有一家咖啡吧生意特别好,每天来的客人络绎不绝,客人a来到柜台,客人b紧随其后,客人c排在客人b后面,客人d排在客人c后面,客人e排在客人d后面,一直排到店面门外。老板和三个员工首先为客人a准备食物:员工甲拿了一个干净的盘子,然后员工乙在盘子里装上薯条,员工丙再在盘子里放上豌豆,老板最后配上一杯饮料,完成对客人a的服务,送走客人a,下一位客人b开始被服务。然后员工甲又拿了一个干净的盘子,员工乙又装薯条,员工丙又放豌豆,老板又配上了一杯饮料,送走客人b,客人c开始被服务。一直重复下去。
从效率方面观察这个现象,当服务客人a时,在员工甲拿了一个盘子后,员工甲一直处于空闲状态,直到送走客人a,客人b被服务。老板自然而然的就会想到如果每个人都不停的干活,就可以服务更多的客人,赚到更多的钱。老板通过不停的尝试想出了一个办法。以客户a,b为例阐述这个方法:员工甲为客户a准备好了盘子后,在员工乙开始为客户a装薯条的同时,员工甲开始为客户b准备托盘。这样员工甲就可以不停的进行生产。整个过程如下图,客户们围着咖啡吧台排队,因为有四个生产者,一个老板加三个员工,所以可以同时服务四个客户。我们将目光转向老板,单位时间从他那里出去的客户数提高了将近四倍,也就是说效率提高将近四倍。
这样子,我们就很好理解了pipeline了,那其实就是每个人络绎不绝的做自己的事情,但个人事情又是整个流程的一部分,有点像工厂的小妹,只做包鞋底,但是这又是制造鞋的中间流程。一条流水,一条管道,一直处理下去。那每个http请求,到了iis也是这样子的。
pipeline模型的缺点
每次它对于一个输入(或者一次请求)都必须从链头开始遍历(參考http server处理请求就能明确),这确实存在一定的性能损耗。
当服务器接收到一个http请求的时候,iis是如何去决定,并处理该请求。答案就是文件的“后缀名”。
服务器获取所请求的页面或文件的后缀名后,那服务器就回去寻找能出来这些后缀的应用程序。若是iis找不到,并且文件没有受到iis的保护(保护:app_code文件夹的文件,不保护:平常的js文件等),就会直接返回给客户端(浏览器,移动端等)
那么能处理后缀名的程序叫什么呢? isapi 应用程序(internet server application programe interface,互联网服务器应用程序接口)。它其实是一个接口,起到一个代理的作用,他的主要工作就是将请求的页面(文件)与处理该页面(文件)的后缀的处理程序进行一个映射。让其可以争取的去处理页面(文件)。
那这个应用程序长什么样子呢?这边我们能看到后缀名与之可执行的文件路径。
接着我们找到“.axpx”的后缀名,打开来看。
我们可以发现".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请求,从开始到结束。
如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复
Blazor server side 自家的一些开源的, 实用型项目的进度之 CEF客户端
.NET IoC模式依赖反转(DIP)、控制反转(Ioc)、依赖注入(DI)
vue+.netcore可支持业务代码扩展的开发框架 VOL.Vue 2.0版本发布
网友评论