通常咱們都在寫業務代碼,優化頁面,優化邏輯之間內徘徊。也許咱們懂得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請求,從開始到結束。