我們在基於asp.net開發web程式,基本上都是發佈部署到安裝了IIS的windows伺服器上,然後只要用戶能夠訪問就算任務完成了,但是很少靜下心來想想這背後到底發生了什麼,那麼這個系列就來總結下asp.net的基礎原理。 asp.net是什麼 我們做web開發的可以說時時刻刻都在跟asp.net ...
我們在基於asp.net開發web程式,基本上都是發佈部署到安裝了IIS的windows伺服器上,然後只要用戶能夠訪問就算任務完成了,但是很少靜下心來想想這背後到底發生了什麼,那麼這個系列就來總結下asp.net的基礎原理。
asp.net是什麼
我們做web開發的可以說時時刻刻都在跟asp.net打交道,但很少總結asp.net是什麼,可以用一句話總結: asp.net是一個開發web程式的平臺。HTTP協議
由於web程式是基於HTTP協議的,所以在繼續深入瞭解asp.net之前有必要學習下HTTP協議。 HTTP協議是一個屬於應用層,基於TCP/IP協議,用於從萬維網伺服器傳輸超文本到本地瀏覽器的傳送協議。HTTP協議工作於客戶端/服務端架構上,如下圖。
HTTP協議具有以下顯著特點:
1,簡單快速,客戶端向服務端發起請求時,只需發送請求方法和url。常見請求方法包括:GET,POST等。 2,靈活,是因HTTP協議允許發送任意類型的數據對象,數據類型可以通過Content-Type指定。 3,無連接和無狀態。無連接的意思是,每次連接只處理一個請求,服務端處理完請求並且客戶端收到請求後即斷開連接。無狀態的意思是,每次的請求都是獨立的。 4,一般都是基於B/S架構的。IIS與asp.net
asp.net與IIS有著密不可分的聯繫的。 1,IIS 5.x與asp.net IIS 5.x是集成在windows server 2000上的。 首先看下麵的圖,這張圖展示了在IIS 5.x下如何處理asp.net程式請求的。當檢測到某個HTTP Request後,先根據擴展名判斷請求的是否是靜態資源(比如.html,.img,.txt,.xml等),如果是則直接將文件內容以HTTP Response的形式返回。如果是動態資源(比如.aspx,asp,php等等),則通過擴展名從IIS的腳本影射(Script Map)找到相應的ISAPI Dll。
ISAPI是Internet伺服器API(Internet Server Application Programming Interface)的縮寫,是一套本地的(Native)Win32 API,具有較高的執行性能,是IIS和其他動態Web應用或者平臺之間的紐帶。比如ASP ISAPI橋接IIS與ASP,而ASP.NET ISAPI則連接著IIS與ASP.NET。ISPAI定義在一個Dll中,ASP.NET ISAPI對應的Dll為Aspnet_isapi.dll,你可以在目錄“%windir%\Microsoft.NET\Framework\{version no}\”中找到該Dll。
ISAPI支持ISAPI擴展(ISAPI Extension)和ISAPI篩選(ISAPI Filter),前者是真正處理HTTP請求的介面,後者則可以在HTTP請求真正被處理之前查看、修改、轉發或者拒絕請求,比如IIS可以利用ISAPI篩選進行請求的驗證(Authentication)。
如果我們請求的是一個基於ASP.NET的資源類型,比如:.aspx Web Page、 .asmx Web Service或者.svc WCF Service等,Aspnet_isapi.dll會被載入,ASP.NET ISAPI擴展會創建ASP.NET的工作進程(如果該進程尚未啟動),對於IIS 5.x來說,該工作進程為aspnet.exe。IIS進程與工作進程之間通過命名管道(Named Pipes)進程通信,以獲得最好的性能。
在工作進程初始化過程中,.NET 運行時(CLR)被載入,從而構建了一個托管的環境。對於某個Web應用的初次請求,CLR會為其創建一個AppDomain。在此AppDomain中,HTTP運行時(HTTP Runtime)被載入並用以創建相應的應用。對於寄宿於IIS 5.x的所有Web 應用都運行在同一個進程(工作進程aspnet_wp.exe)的不同AppDomain中。
2,IIS 6.0與asp.net IIS 6.0是集成在windows server 2003上的。 從上面可以看到,IIS 5.x仍存在以下兩個問題:- ISAPI Dll被載入到InetInfo.exe進程中,它和工作進程之間是一種典型的跨進程通信方式,儘管採用性能最好的命名管道,但是仍然會帶來性能的瓶頸。
- 所有的ASP.NET應用,運行在相同的進程(aspnet_wp.exe)中的不同的應用程式域(AppDomain)中,基於應用程式域的隔離級別不能從根本上解決一個應用程式對另一個程式的影響,在更多的時候,我們需要不同的Web應用運行在不同的進程中。
那麼IIS 6.0下是如何處理asp.net請求的呢,如下圖。
在IIS 6.0中,為瞭解決第一個問題,ISAPI.dll被直接載入到工作進程中。為瞭解決第2個問題,引入了應用程式池(Application Pool)的機制。我們可以為一個或者多個Web應用創建應用程式池,每一個應用程式池對應一個獨立的工作進程,從而為運行在不同應用程式池中的Web應用提供基於進程的隔離級別。IIS 6.0的工作進程名稱為w3wp.exe。
當然,除了上面兩點改進之外,IIS 6.0還有其他一些值得稱道的地方,其中最重要的一點就是創建了一個新的HTTP監聽器:HTTP協議棧(HTTP Protocol Stack,HTTP.SYS)。HTTP.SYS運行在Windows的內核模式(Kernel Mode)下,作為驅動程式而存在。它是Windows 2003的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會把響應的內容進行緩存,緩存的內容可以直接響應後續的請求。由於這是基於內核模式的緩存,不存在內核模式和用戶模式的切換,響應速度將得到極大的改進。
圖2體現了IIS的結構和處理HTTP請求的流程。從中可以看出,與IIS 5.x不同,W3SVC從InetInfo.exe進程脫離出來(對於IIS6.0來說,InetInfo.exe基本上可以看作單純的IIS管理進程),運行在另一個進程SvcHost.exe中。不過W3SVC的基本功能並沒有發生變化,只是在功能的實現上作了相應的改進。與IIS 5.x一樣,元資料庫(Metabase)依然存在於InetInfo.exe進程中。
當HTTP.SYS監聽到用戶的HTTP請求後,將其分發給W3SVC。W3SVC解析出請求的URL,並根據從Metabase獲取的URL與Web應用之間的映射關係得到目標應用,併進一步得到目標應用運行的應用程式池或者工作進程。如果工作進程不存在(尚未創建或者被回收),則為該請求創建新的工作進程,工作進程的這種創建方式被稱為請求式創建。在工作進程的初始化過程中,相應的ISAPI.dll被載入,對於ASP.NET應用來說,被載入的ISAPI.dll為Aspnet_ispai.dll。ASP.NET ISAPI再負責進行CLR的載入、AppDomain創建、Web Application的初始化等。
所以,相比IIS 5.x,IIS 6.0的變化主要在以下方面:
- ISAPI.dll被直接載入到工作進程中,這解決了IIS 5.x的第1個問題。
- 引入了應用程式池(Application Pool)的機制。
- 創建了一個新的HTTP監聽器,HTTP協議棧(HTTP Protocol Stack,即HTTP.sys)
IIS 7.0對請求的監聽和分發機制上又進行了革新性的改進,主要體現在對於Windows進程激活服務(Windows Process Activation Service,WAS)的引入,將原來(IIS 6.0)W3SVC承載的部分功能分流給了WAS。具體來說,通過上面的介紹,我們知道對於IIS 6.0來說,W3SVC主要承載著三大功能:
- HTTP請求接收:接收HTTP.sys監聽到的HTTP請求;
- 配置管理:從元資料庫(Metabase)中載入配置信息對相關組件進行配置;
- 進程管理:創建、回收、監控工作進程。
在IIS 7.0,後兩組功能被移入WAS中,接收HTTP請求的任務依然落在W3SVC頭上。WAS的引入為IIS 7.0一項前所未有的特性:同時處理HTTP和非HTTP請求。在WAS中,通過一個重要的介面:監聽器適配器介面(Listener Adapter Interface)抽象出不同協議監聽器監聽到的請求。至於IIS下的監聽器,除了基於網路驅動的HTTP.SYS提供HTTP請求監聽功能外,WCF提供了3種類型的監聽器:TCP監聽器、命名管道(Named Pipes)監聽器和MSMQ監聽器,分別提供了基於TCP、命名管道和MSMQ傳輸協議的監聽功能。與此3種監聽器相對的,是3種監聽器適配器(Adapter)提供監聽器與監聽器適配器介面之間的適配。從這個意義上講,IIS 7.0中的W3SVC更多地為HTTP.SYS起著監聽適配器的功能。WCF提供的這3種監聽器和監聽適配器定義在程式集SMHost.exe中,你可以通過下麵的目錄找到該程式集:%windir%\Microsoft.NET\Framework\v3.0\Windows Communication Foundatio。
WCF提供的這3種監聽器和監聽適配器最終以Windows Service的形式體現,雖然它們定義在一個程式集中,我們依然通過服務工作管理器(SCM,Service Control Manager)對其進行單獨的啟動、終止和配置。SMHost.exe提供了4個重要的Windows Service:
- NetTcpPortSharing:為WCF提供TCP埠共用,關於埠共用;
- NetTcpActivator:為WAS提供基於TCP的激活請求,包含TCP監聽器和對應的監聽適配器;
- NetPipeActivator:為WAS提供基於命名管道的激活請求,包含命名管道監聽器和對應的監聽適配器;
- NetMsmqActivator:為WAS提供基於MSMQ的激活請求,包含MSMQ監聽器和對應的監聽適配器。
下圖為上述的4個Windows Service在服務控制管理器(SCM)中的呈現。
第1張圖揭示了IIS 7.0的整體構架以及整個請求處理流程。無論是從W3SVC接收到的HTTP請求,還是通過WCF提供的監聽適配器接收到的請求,最終都會傳遞到WAS。如果相應的工作進程(或者應用程式池)尚未創建,其創建之;否則將請求分發給對應的工作進程進行後續的處理。WAS在進行請求處理過程中,通過內置的配置管理模塊載入相關的配置信息對相關的組建進行配置,與IIS 5.x和IIS 6.0基於Metabase的配置信息存儲不同的是,IIS 7.0大都將配置信息存放於XML形式的配置文件中。基本的配置存放在applicationHost.cofig中。
所以,相比IIS 6.0,IIS 7.0的變化主要在以下方面:
- 引入了Windows進程激活服務(Windows Process Activation Service,WAS),將原來(IIS 6.0)W3SVC承載的部分功能分流給了WAS,帶來的好處是,可以同時處理HTTP請求和非HTTP請求。
- 與IIS 5.x和IIS 6.0基於Metabase的配置信息存儲不同的是,IIS 7.0大都將配置信息存放於XML形式的配置文件中。基本的配置存放在applicationHost.cofig中。
asp.net管道
先看兩張圖。asp.net管道
以IIS 6.0為例,在工作進程w3wp.exe中,利用Aspnet_ispai.dll載入.NET運行時(如果.NET運行時尚未載入)。IIS 6引入了應用程式池的概念,一個工作進程對應著一個應用程式池。一個應用程式池可以承載一個或者多個Web應用,每個Web應用映射到一個IIS虛擬目錄。與IIS 5.x一樣,每一個Web應用運行在各自的應用程式域中。
如果HTTP.SYS接收到的HTTP請求是對該Web應用的第一次訪問,當成功載入了運行時後,會通過AppDomainFactory為該Web應用創建一個應用程式域(AppDomain)。隨後,一個特殊的運行時IsapiRuntime被載入。IsapiRuntime定義在程式集System.Web中,對應的命名空間為System.Web.Hosting。IsapiRuntime會接管該HTTP請求。
IsapiRuntime會首先創建一個IsapiWorkerRequest對象,用於封裝當前的HTTP請求,並將該IsapiWorkerRequest對象傳遞給ASP.NET運行時:HttpRuntime,從此時起,HTTP請求正式進入了ASP.NET管道。根據IsapiWorkerRequest對象,HttpRuntime會創建用於表示當前HTTP請求的上下文(Context)對象:HttpContext。
隨著HttpContext被成功創建,HttpRuntime會利用HttpApplicationFactory創建新的或者獲取現有的HttpApplication對象。實際上,ASP.NET維護著一個HttpApplication對象池,HttpApplicationFactory從池中選取可用的HttpApplication用戶處理HTTP請求,處理完畢後將其釋放到對象池中。HttpApplicationFactory負責處理當前的HTTP請求。
在HttpApplication初始化過程中,會根據配置文件載入並初始化相應的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。上面整個處理流程如第1圖所示。
HttpApplication
HttpApplication是整個ASP.NET基礎架構的核心,它負責處理分發給它的HTTP請求。由於一個HttpApplication對象在某個時刻只能處理一個請求,只有完成對某個請求的處理後,HttpApplication才能用於後續的請求的處理。所以,ASP.NET採用對象池的機制來創建或者獲取HttpApplication對象。具體來講,當第一個請求抵達的時候,ASP.NET會一次創建多個HttpApplication對象,並將其置於池中,選擇其中一個對象來處理該請求。當處理完畢,HttpApplication不會被回收,而是釋放到池中。對於後續的請求,空閑的HttpApplication對象會從池中取出,如果池中所有的HttpApplication對象都處於繁忙的狀態,ASP.NET會創建新的HttpApplication對象。
HttpApplication處理請求的整個生命周期是一個相對複雜的過程,在該過程的不同階段會觸發相應的事件。我們可以註冊相應的事件,將我們的處理邏輯註入到HttpApplication處理請求的某個階段。我們接下來介紹的HttpModule就是通過HttpApplication事件註冊的機制實現相應的功能的。表1按照實現的先後順利列出了HttpApplication在處理每一個請求時觸發的事件名稱。
經典的asp.net管道的19個事件:
名稱 |
描述 |
BeginRequest |
HTTP管道開始處理請求時,會觸發BeginRequest事件 |
AuthenticateRequest,PostAuthenticateRequest |
ASP.NET先後觸發這兩個事件,使安全模塊對請求進行身份驗證 |
AuthorizeRequest,PostAuthorizeRequest |
ASP.NET先後觸發這兩個事件,使安全模塊對請求進程授權 |
ResolveRequestCache,PostResolveRequestCache |
ASP.NET先後觸發這兩個事件,以使緩存模塊利用緩存的直接對請求直接進程響應(緩存模塊可以將響應內容進程緩存,對於後續的請求,直接將緩存的內容返回,從而提高響應能力)。 |
PostMapRequestHandler |
對於訪問不同的資源類型,ASP.NET具有不同的HttpHandler對其進程處理。對於每個請求,ASP.NET會通過擴展名選擇匹配相應的HttpHandler類型,成功匹配後,該實現被觸發 |
AcquireRequestState,PostAcquireRequestState |
ASP.NET先後觸發這兩個事件,使狀態管理模塊獲取基於當前請求相應的狀態,比如SessionState |
PreRequestHandlerExecute,PostRequestHandlerExecute |
ASP.NET最終通過一請求資源類型相對應的HttpHandler實現對請求的處理,在實行HttpHandler前後,這兩個實現被先後觸發 |
ReleaseRequestState,PostReleaseRequestState |
ASP.NET先後觸發這兩個事件,使狀態管理模塊釋放基於當前請求相應的狀態 |
UpdateRequestCache,PostUpdateRequestCache |
ASP.NET先後觸發這兩個事件,以使緩存模塊將HttpHandler處理請求得到的相應保存到輸出緩存中 |
LogRequest,PostLogRequest |
ASP.NET先後觸發這兩個事件為當前請求進程日誌記錄 |
EndRequest |
整個請求處理完成後,EndRequest事件被觸發 |
對於一個ASP.NET應用來說,HttpApplication派生於global.asax文件,我們可以通過創建global.asax文件對HttpApplication的請求處理行為進行定製。global.asax採用一種很直接的方式實現了這樣的功能,這種方式既不是我們常用的方法重寫(Method Overriding)或者事件註冊,而是直接採用方法名匹配。在global.asax中,我們按照這樣的方法命名規則進行事件註冊:Application_{Event Name}。比如Application_BeginRequest方法用於處理HttpApplication的BeginRequest事件。如果通過VS創建一個global.asax文件,下麵是預設的定義。
1 <%@ Application Language="C#" %> 2 <script runat="server"> 3 void Application_Start(object sender, EventArgs e) {} 4 void Application_End(object sender, EventArgs e) {} 5 void Application_Error(object sender, EventArgs e) {} 6 void Session_Start(object sender, EventArgs e) {} 7 void Session_End(object sender, EventArgs e) {} 8 </script>
HttpModule
ASP.NET為創建各種.NET Web應用提供了強大的平臺,它擁有一個具有高度可擴展性的引擎,並且能夠處理對於不同資源類型的請求。那麼,是什麼成就了ASP.NET的高可擴展性呢? HttpModule功不可沒。
從功能上講,HttpModule之於ASP.NET,就好比ISAPI Filter之於IIS一樣。IIS將接收到的請求分發給相應的ISAPI Extension之前,註冊的ISAPI Filter會先截獲該請求。ISAPI Filter可以獲取甚至修改請求的內容,完成一些額外的功能。與之相似地,當請求轉入ASP.NET管道後,最終負責處理該請求的是與請求資源類型相匹配的HttpHandler對象,但是在Handler正式工作之前,ASP.NET會先載入並初始化所有配置的HttpModule對象。HttpModule在初始化的過程中,會將一些功能註冊到HttpApplication相應的事件中,那麼在HttpApplication整個請求處理生命周期中的某個階段,相應的事件會被觸發,通過HttpModule註冊的事件處理程式也得以執行。
所有的HttpModule都實現了IHttpModule介面,下麵是IHttpModule的定義。其中Init方法用於實現HttpModule自身的初始化,該方法接受一個HttpApplication對象,有了這個對象,事件註冊就很容易了。
1 public interface IHttpModule 2 { 3 void Dispose(); 4 void Init(HttpApplication context); 5 }
ASP.NET提供的很多基礎構件(Infrastructure)功能都是通過相應的HttpModule實現的,下麵類列出了一些典型的HttpModule:
- OutputCacheModule:實現了輸出緩存(Output Caching)的功能;
- SessionStateModule:在無狀態的HTTP協議上實現了基於會話(Session)的狀態;
- WindowsAuthenticationModule + FormsAuthenticationModule + PassportAuthentication- Module:實現了3種典型的身份認證方式:Windows認證、Forms認證和Passport認證;
- UrlAuthorizationModule + FileAuthorizationModule:實現了基於Uri和文件ACL(Access Control List)的授權。
而另外一個重要的HttpModule與WCF相關,那麼就是System.ServiceModel. Activation.HttpModule。HttpModule定義在System.ServiceModel程式集中,在預設的情況下,HttpModule完成了基於IIS的寄宿工作。
除了這些系統定義的HttpModule之外,我們還可以自定義HttpMoudle。通過Web.config,我們可以很容易地將其註冊到我們的Web應用中。
HttpHandler
如果說HttpModule相當於IIS的ISAPI Filter的話,我們可以說HttpHandler則相當於IIS的ISAPI Extension,HttpHandler在ASP.NET中扮演請求的最終處理者的角色。對於不同資源類型的請求,ASP.NET會載入不同的Handler來處理,也就是說.aspx page與.asmx web service對應的Handler是不同的。
所有的HttpHandler都實現了介面IHttpHandler。下麵是IHttpHandler的定義,方法ProcessRequest提供了處理請求的實現。
1 public interface IHttpHandler 2 { 3 void ProcessRequest(HttpContext context); 4 bool IsReusable { get; } 5 }
對於某些HttpHandler,具有一個與之相關的HttpHandlerFactory,用於創建或者獲取相應的HttpHandler。HttpHandlerFactory實現介面IHttpHandlerFactory,方法GetHandler用於創建新的HttpHandler,或者獲取已經存在的HttpHandler。
1 public interface IHttpHandlerFactory 2 { 3 IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated); 4 void ReleaseHandler(IHttpHandler handler); 5 }
HttpHandler和HttpHandlerFactory的類型都可以通過相同的方式配置到Web.config中。下麵一段配置包含對3種典型的資源類型的HttpHandler配置:.aspx,.asmx和.svc。可以看到基於WCF Service的HttpHandler類型為:System.ServiceModel.Activation.HttpHandler。
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <system.web> 4 <httpHandlers> 5 <add path="*.svc" verb="*" type="System.ServiceModel.Activation.HttpHandler, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" validate="false"/> 6 <add path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" validate="True"/> 7 <add path="*.asmx" verb="*" type="System.Web.Services.Protocols.WebServiceHandlerFactory, System.Web.Services, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" validate="False"/> 8 </httpHandlers> 9 </system.web> 10 </configuration>
參考資料
- http://www.cnblogs.com/artech/archive/2009/06/20/1507165.html
- http://www.cnblogs.com/OceanEyes/archive/2012/08/13/aspnetEssential-1.html
- http://www.cnblogs.com/wenthink/archive/2013/05/06/HTTP_IIS_ASPNET_Pipeline.html