IIS/ASP.NET 管道
ASP.NET MVC 是建立在 ASP.NET 平臺上基于 MVC 模式的 Web 應用框架,深刻理解 ASP.NET MVC 的前提是對 ASP.NET 管道式設計具有深刻的認識。由于 ASP.NET Web 應用大都寄宿于 IIS 上,將兩者結合起來了解在 IIS 和 ASP.NET 管道中是如何流動的。
IIS5.x與ASP.NET
IIS5.x 是如何處理基于 ASP.NET 資源(如.aspx、.asmx...)請求的呢?
IIS5.x運行在進程 InetInfo.exe 中,該進程寄存著一個名為 World Wide Web Publishing Service(簡稱W3SVC)的 Windows 服務。W3SVC 主要負責 HTTP 請求的監聽、激活、管理工作進程、加載配置(從 Metabase 中加載相關配置信息)等。
IIS5.x與ASP.NET
當檢測到某個 HTTP 請求時,IIS 先根據擴展名判斷請求的是靜態資源還是動態資源。對于靜態資源 IIS 會將文件內容直接響應給客戶端,對于動態資源則通過擴展名從 IIS 的腳本映射(Script Map)中找到相應的ISAPI 動態鏈接庫(Dynamic Link Library, DLL)。
ISAPI(Internet Server Application Programming Interface)是一套本地的(Native)Win32 API,是 IIS 和其他動態 Web 應用或平臺之間的紐帶。
ISAPI 定義在一個動態鏈接庫(DLL)文件中, ASP.NET ISAPI 對應的 DLL 文件名稱為 aspnet_isapi.dll,位于 %windir%\Microsoft.NET\Framework\{version no}\中。
ISAPI 支持 ISAPI 擴展(ISAPI Extension)和 ISAPI 篩選(ISAPI Filter),前者是真正處理 HTTP 請求的接口,后者則可以在 HTTP 請求真正被處理之前查看、修改、轉發、拒絕請求,比如 IIS 可利用 ISAPI 篩選進行請求的驗證。
若請求的是一個基于 ASP.NET 的資源類型,如.aspx、.asmx、.svc等,aspnet_isapi.dll 會被加載,ASP.NET ISAPI 隨后會創建 ASP.NET 的工作進程(若該進程尚未啟動)。對于 IIS5.x 來說,該工作進程為 aspnet.exe。IIS 進程與工作進程之間通過命名管道(Named Pipes)進行通信。
在工作進程初始化過程中,.NET 運行時(CLR)會被加載以構建一個托管的環境。對于某個 Web 應用的初次請求,CLR 會被其創建一個應用程序域(Application Domain)。
在應用程序域中,HTTP 運行時(HTTP Runtime)被加載并用以創建相應的應用。寄存于 IIS5.x 的所有Web應用都運行在同一個進程(工作進程 aspnet_wp.exe)的不同應用程序域中。
IIS6.0 與 ASP.NET
IIS5.x存在兩個方面的不足:
- ISAPI動態鏈接庫被加載到 InetInfo.exe 進程中,它和工作進程之間是一種典型的跨進程通信方式,盡管采用命名管道,仍會帶來性能瓶頸。
解決方案:IIS6.0將ISAPI動態鏈接庫直接加載到工作進程中
- ASP.NET 應用運行在相同進程 aspnet_wp.exe 的不同程序域中,基于應用程序域的隔離不能從根本上解決一個應用程序對另一個程序的影響,需不同的Web應用運行在不同的進程中。
解決方案:IIS6.0 加入了應用程序池 Application Pool 的機制,為一個或多個 Web 應用創建一個應用程序池,每個應用程序池對應一個獨立的工作進程 w3wp.exe,所以運行在不同應用程序池中的 Web 應用提供基于進程級別的隔離機制。
HTTP.SYS
IIS6.0 中最重要的一點是創建了一個名為 HTTP.SYS 的 HTTP 監聽器,HTTP.SYS 以驅動程序的形式運行在Windows 的內核模式 Kernal Mode下,它是 Windows TCP/IP 網絡子系統的一部分,從結構上看它屬于 TCP 之上的一個網絡驅動程序。
嚴格地說,HTTP.SYS 已經不屬于 IIS 的范疇了,所以 HTTP.SYS 的配置信息也沒有保存在 IIS 的元數據庫Metabase,而是定義在注冊表中。HTTP.SYS 的注冊表項的路徑為HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/HTTP。
HTTP.SYS能夠帶來如下好處:
- 持續監聽
由于 HTTP.SYS 是一個網路驅動程序,始終處于運行狀態,所以對于用戶會的 HTTP 請求能夠及時作出反應。
- 穩定性
HTTP.SYS 運行在操作系統內核模式下,并不執行任何用戶代碼,其本身不會受到 Web應用、工作進程、IIS進程的影響。
- 內核模式下的數據緩存
如果某個資源被頻繁請求,HTTP.SYS 會把響應的內容進行緩存,緩存的內容可直接響應后續的請求。由于這是基于內核模式的緩存,不存在內核模式和用戶模式的切換,響應速度得到極大的改進。
IIS6.0結構
與 IIS5.x 不同,W3SVC 在 IIS6.0 中從 InetInfo.exe 進程脫離出來,對于 IIS6.0 來說,InetInfo.exe基本上可以看做單純的 IIS 管理進程,運行在另一個進程 SvcHost.exe中。不過 W3SVC 的基本功能并沒有發生變化,只是在功能的實現上作出相應的改進。與 IIS5.x 一樣,元數據庫 Metabase 依然存在于InetInfo.exe進程中。
IIS6.0與ASP.NET
IIS6.0處理HTTP請求的流程
當監聽到HTTP請求時,HTTP.SYS 將其分發給 W3SVC,W3SVC 解析出請求的 URL ,并根據從 Metabase 獲取的 URL 與 Web 應用之間的映射關系得到目標應用,進而得到目標應用運行的應用程序池或工作進程。如果工作進程不存在或尚未創建或被回收,它為請求創建新的工作進程。在工作進程的初始化過程中,相應的 ISAPI 動態鏈接庫被加載,對于 ASP.NET 應用來說,被加載的 ISAPI.dll 為 aspnet_isapi.dll。ASP.NET ISAPI 負責進行 CLR 的加載、應用程序域的創建和 Web 應用的初始化等操作。
IIS7.0與ASP.NET
IIS7.0在請求的監聽和分發機制上進行了革新,體現在引入Windows進程激活服務(Windows Process Activation Service, WAS),分流了原來IIS6.0中W3SVC承載的部分功能。
IIS6.0中的W3SVC主要承載著如下三個功能:
- HTTP請求接收:接收HTTP.SYS監聽到的HTTP請求
- 配置管理:從元數據Metabase中加載配置信息對相關組件進行配置
- 進程管理:創建、回收、監控工作進程
IIS7.0將后兩組功能實現到了WAS中,但接收HTTP請求的任務依然落在W3SVC上。WAS的引入為IIS7.0提供了對非HTTP協議的支持,通過監聽適配器接口 Listener Adapter Interface抽象出針對不同協議的監聽器。具體來說或,除了專門用于監聽HTTP請求的HTTP.SYS之外,WAS利用TCP監聽器、命名管道監聽器和MSMQ監聽器提供基于TCP、命名管道、MSMQ傳輸協議的監聽支持。
與此3種監聽器相對應的是3種監聽適配器,它們提供監聽器與WAS中的監聽適配器接口之間的適配,從這個意義上講,IIS7.0中的W3SVC相當于HTTP.SYS的監聽適配器。這3種非HTTP監聽器和監聽適配器定義在程序集 SMSvcHost.exe 中??稍谀夸?windir%\Microsoft.NET\Framework\v3.0\Windows Communication Foundation\中找到。
從程序集所在的目錄名稱可以看出,這3種監聽器/監聽適配器是為WCF設計的,它們以Windows服務的形式進行工作。雖然定義在一個程序集中。但依然可通過服務工作管理器對其進行單獨的啟動、終止、配置。總的來說,SMSvcHost.exe提供了4個重要的Windows Service。
- NetTcpPortSharing
為WCF提供TCP端口共享,即同一個監聽端口被多個進程共享。 - NetTcpActivator
為WAS提供基于TCP的激活請求,包含TCP監聽器和對應的監聽適配器。 - NetPipeActivator
為WAS提供基于命名管道的激活請求,包含命名管道和對應的監聽適配器。 - NetMsmqActivator
為WAS提供基于MSMQ的激活請求,包含MSMQ監聽器和對應的監聽適配器。
Windows Service
IIS7.0架構
IIS7.0與ASP.NET
無論是從W3SVC接收到的HTTP請求,還是通過WCF提供的監聽適配器接收到的針對其他傳輸協議的請求,最終都胡被傳遞到WAS。如果相應的工作進程,針對單個應用程序池尚未創建,則WAS會創建工作進程。WAS在進行請求處理過程中通過內置的配置管理模塊加載相關的配置信息,并對相關的組件進行配置。
與IIS5.x和IIS6.0基于Metabase的配置信息存儲不同的是,IIS7.0大多將配置信息存放在XML形式的配置文件中,基本的配置存放在 applicationHost.config 中。
API.NET 集成
從IIS5.x和IIS6.0中不難發現,IIS與ASP.NET是兩個獨立的管道 Pipeline。在各自管轄范圍內,各自具有自己的一套機制對HTTP請求進行處理。兩個管道通過ISAPI實現連通。IIS是第一道屏障,當對HTTP請求進行必要的前期處理,如身份認證等。IIS通過ISAPI將請求分發給ASP.NET管道。當ASP.NET在自身管道范圍內完成對HTTP請求的處理時,處理后的結果再返回到IIS,IIS對其進行后期處理,如日志記錄、壓縮等。之后生成HTTP回復對請求予以響應。
IIS6.0與ASP.NET之間的橋接關系 基于IIS6.0和ASP.NET雙管道設計
從另一個角度講,IIS運行在非托管的環境中,而ASP.NET管道則是托管的,所以說ISAPI是連接非托管環境和托管環境的紐帶。IIS5.x和IIS6.0把兩個管道進行隔離帶來的局限的不足是:
- 相同操作的重復執行
IIS和ASP.NET之間具有一些重復的操作,如身份驗證。
- 動態文件與靜態文件處理的不一致
因為只有基于ASP.NET 動態文件的HTTP請求才能通過ASP.NET ISAPI進入ASP.NET管道,對于靜態文件的請求則由IIS直接響應,那么ASP.NET管道中的一些功能將不能作用域這些基于靜態文件的請求,比如通過Forms認證應用于基于圖片文件的請求就做不到。
- IIS難以擴展
對于IIS的擴展基本上提現在自定義ISAPI,對于大部分人來說,這不是一件容易的事情,因為ISAPI是基于Win32的非托管的API,并非一種面向應用的編程接口。通常希望的是諸如定義ASP.NET的HttpModule和HttpHandler一樣,托管代理的方式來擴展IIS。
對于Windows平臺下的IIS來講,ASP.NET無疑是一等公民,IIS7.0中實現了兩者的集成,通過集成可獲得如下好處:
- 允許通過本地代碼Native Code和托管代碼Managed Code兩種方式定義IIS Module
這些IIS Module注冊到IIS中形成一個通用的請求處理管道,由IIS Module組成的這個管道能夠處理所有的請求,無論請求基于怎樣的資源類型。例如,可將FormAuthenticationModule提供的Forms認證應用到基于 .aspx、CGI和靜態文件的請求。
- 將ASP.NET提供的一些強大的功能應用到原來難以企及的地方
比如將ASP.NET的URL重寫功能置于身份驗證之前
- 采用相同的方式去實現、配置、檢測和支持一些服務器特性 Feature
比如Module、Handler映射、定制錯誤配置 Custom Error Configuration 等
基于IIS7.0 與 ASP.NET集成管道設計
ASP.NET 管道
以 IIS6.0為例,它的工作進程 w3wp.exe 中會利用 aspnet_isapi.dll 加載 .NET 運行時(如果 .NET 運行時尚未加載)。IIS6.0引入了應用程序池的概念,一個工作進程對應著一個應用程序池。一個應用程序池可以承載一個或多個Web應用,每個Web應用映射到一個IIS虛擬目錄,與IIS5.x一樣,每個Web應用運行在各自的應用程序域中。
ASP.NET 處理管道
如果HTTP.SYS接收到的HTTP請求是對該 Web 應用的第一次訪問,在成功加載運行使之后,IIS會通過 AppDomainFactory 為該Web應用創建一個應用程序域。隨后一個特殊的運行時 IsapiRuntime 被加載。IsapiRuntime 定義在程序集 System.Web.dll中,對應的命名空間為System.Web.Hosting, 被加載的 IsapiRuntime 會接管該HTTP請求。
接管 HTTP 請求的 IsapiRuntime 會首先創建一個 IsapiWorkerRequest 對象來封裝當前的HTTP請求,隨后將次對象傳遞給 ASP.NET 運行時 HttpRuntime。從此時起, HTTP請求正式進入了ASP.NET管道。HttpRuntime會根據 IsapiWorkerRequest 對象創建用于表示當前HTTP請求的上下文 Context 對象 HttpContext。
隨著 HttpContext 的創建,HttpRuntime 會利用 HttpApplicationFactory 創建新的或獲取現有的 HttpApplication 對象。實際上 ASP.NET 維護著一個 HttpApplication 對象池, HttpApplicationFactory 從池中選取可用的 HttpApplication 用于處理 HTTP 請求,處理完畢后將其釋放到對象池中。HttpApplication 負責處理當前的 HTTP 請求。
在 HttpApplication 初始化過程中,ASP.NET 會根據配置文件加載并初始化注冊的 HttpModule 對象。對于 HttpApplication 來說,在它處理HTTP請求的不同階段會觸發不同的事件 Event,而HttpModule的意義在于通過注冊HttpApplication的相應事件,將所需的操作注入整個HTTP請求的處理流程。ASP.NET的很多功能都是通過相應的HttpModule實現的。
最終完成對HTTP請求的處理實現在HttpHandler中,不同的資源類型對應著不同類型的HttpHandler。比如.aspx頁面對應的HttpHandler類型為System.Web.UI.Page,WCF的.svc文件對應的HttpHandler類型為System.ServiceModel.Activation.HttpHandler。
HttpApplication
HttpApplication 是整個 ASP.NET 基礎架構的核心,負責處理分發給它的HTTP請求。由于一個HttpApplication對象在某個時刻只能處理一個請求,只有完成對某個請求的處理后才能用于后續請求的處理,所以ASP.NET采用對象池的機制來創建或獲取 HttpApplication 對象。
當第一個請求抵達時,ASP.NET會一次創建多個HttpApplication對象,并將其置于池中,然后選擇其中一個對象來處理該請求。處理完畢后,HttpApplication不會被回收,而是釋放到池中。對于后續的請求,空閑的 HttpApplication 對象會從池中取出。如果池中所有的 HttpApplication 對象都處于繁忙的狀態,在沒有超出 HttpApplication 池最大容量的情況下,ASP.NET 會創建新的HttpApplication對象,否則將請求放入隊列等待現有HttpApplication的釋放。
HttpApplication 處理請求的整個生命周期是一個相對復雜的過程,在該過程的不同階段會觸發相應的事件。可注冊相應的事件,將處理邏輯注入到HttpApplication處理請求的某個階段。
HttpApplication在處理每個請求時觸發的事件
-
BeginRequest
HTTP管道開始處理請求時,會觸發BeginRequest事件。 -
AuthenticateRequest, PostAuthenticateRequest
ASP.NET 先后觸發這兩個事件,使安全模塊對請求進行身份驗證。 -
AuthorizeRequest, PostAuthorizeRequest
ASP.NET先后觸發這兩個事件,使安全模塊對請求進行授權。 -
ResolveRequestCache, PostResolveRequestCache
ASP.NET 先后觸發這兩個事件,以使緩存模塊利用緩存的內容對請求直接進行響應(緩存模塊可將響應內容進行緩存,對于后續的請求,直接將緩存的內容返回,從而提高響應能力。) -
PostMapRequestHandler
對于訪問不同資源類型,ASP.NET具有不同的HttpHandler對其進行處理,對于每個請求,ASP.NET會通過擴展名選擇匹配響應的HttpHandler類型,成功匹配后,該事件被觸發。 -
AcquireRequestState, PostAcquestRequestState
ASP.NET先后觸發這兩個事件,使狀態管理模塊獲取基于當前請求相應的狀態,如SessionState。 -
PreRequestHandlerExecute、PostRequestHandlerExecute
ASP.NET 最終通過與請求資源類型相對應的 HttpHandler 實現對請求的處理,在實行 HttpHandler 前后,這兩個事件被先后觸發。 -
ReleaseRequestState、PostReleaseRequestState
ASP.NET 先后觸發這兩個事件,使狀態管理模塊釋放基于當前請求響應的狀態。 -
UpdateRequesetCache、PostUpdateRequestCache
ASP.NET先后觸發這兩個事件,以使緩存模塊將HttpHandler處理請求得到的內容得以保存到輸出緩存中。 -
LogRequest、PostLogRequest
ASP.NET先后觸發這兩個事件為當前請求進行日志記錄 -
EndRequest 整個請求處理完成后,EndRequest事件會觸發。
對于一個ASP.NET應用來說,HttpApplication派生于Global.asax文件,通過創建Global.asax文件對HttpApplication的請求處理行為進行定制。Global.asax采用一種很直接的方式實現了這樣的功能,這種方式不是方法重寫或事件注冊,而是直接采用方法名匹配。在Global.asax中按照“Application_{Event Name}”的命名規則進行事件注冊。
?
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Http; using System.Web.Mvc; using System.Web.Optimization; using System.Web.Routing;namespace Huyoo.Backend {// 注意: 有關啟用 IIS6 或 IIS7 經典模式的說明,// 請訪問 http://go.microsoft.com/?LinkId=9394801public class MvcApplication : System.Web.HttpApplication{protected void Application_Start(){AreaRegistration.RegisterAllAreas();WebApiConfig.Register(GlobalConfiguration.Configuration);FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);RouteConfig.RegisterRoutes(RouteTable.Routes);BundleConfig.RegisterBundles(BundleTable.Bundles);}} }HttpModule
ASP.NET擁有一個具有高度的可擴展的引擎,能處理不同資源類型的請求,HttpModule是功不可沒的。
當請求轉入ASP.NET管道時,最終負責處理該請求的是與請求資源類型相匹配的HttpHandler對象,但是在HttpHandler正式工作之前。ASP.NET會先加載并初始化所有配置的HttpModule對象。HttpModule在初始化過程中,會將一些回調操作注冊到HttpApplication相應的事件中。在HttpApplication請求處理生命周期中的某個階段,相應的事件會被觸發,通過HttpModule注冊的事件處理程序也得以執行。
所有的HttpModule都實現了具有如下定義的System.Web.IHttpModule接口,其Init方法實現了針對自身的初始化。該方法接受一個HttpApplication對象,有了這個對象,事件注冊就很容易了。
?
public interface IHttpModule {void Dispose();void Init(HttpApplication context); }ASP.NET提供了很多基礎功能是通過相應的HttpModule實現的。除了系統定義的HttpModule之外,可自定義HttpModule,通過 Web.config 可很容易地將其注冊到 Web 應用中。
- OutputCacheModule
實現了輸出緩存(Output Caching)的功能 - SessionStateModule
在無狀態的HTTP協議上實現了基于會話Session的狀態保持 - WindowsAuthenticationModule + FormsAuthenticationModule + PassportAuthenticationModule
實現了Windows、Forms、Passport三種典型的身份認證方式 - UrlAuthorizationModule + FileAuthorizationModule
實現了基于URL和文件ACL(Access Control List)的授權
HttpHandler
對于不同資源類型的請求,ASP.NET會加載不同的Handler來處理,比如.aspx頁面與.asmx Web服務對應的Handler是不同的。
所有的HttpHandler都實現了具有如下定義的接口 System.Web.IHttpHandler,定義其中的方法ProcessRequest提供了處理請求的實現。
另一個代表異步版本的HttpHandler的IHttpAsyncHandler接口繼承自IHttpHandler,它通過調用BeginProcessRequest/EndProcessRequest方法以異步的方式處理請求。
?
public interface IHttpHandler {void ProcessRequest(HttpContext context);bool IsReusable{ get; } } public interface IHttpAsyncHandler:IHttpHandler {IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData);void EndProcessRequest(IAsyncResult result); }某些 HttpHandler 具有一個與之相關的HttpHandlerFactory,后者實現了具有如下定義的接口 System.Web.IHttpHandlerFactory,定義其中的方法GetHandler用于創建新的HttpHandler或獲取已經存在的HttpHandler。
?
public interface IHttpHandlerFactory {IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTransslated);void ReleaseHandler(IHttpHandler handler); }HttpHandler和HttpHandlerFactory的類型都可以通過相同的方式配置到 Web.config中。
在調用當前HttpContext的RemapHandler方法時指定一個具體的HttpHandler對象,是為了讓ASP.NET直接跳過默認的HttpHandler映射操作。此外,由于這個默認的HttpHandler映射發生在HttpApplication的PostMapRequestHandler事件觸發之前,所以只有在這之前調用RemapHandler方法才有意義。
作者:JunChow520
鏈接:https://www.jianshu.com/p/85c7c931a3e7
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
總結
以上是生活随笔為你收集整理的IIS/ASP.NET 管道的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: iPhone 14上热搜!供应链称已全系
- 下一篇: 小米又买房了!官方晒新园区照片:全玻璃幕