在年月份我曾經寫了三篇詳細介紹IIS架構和ASPNET運行時管道的文章深入介紹了IIS x與IIS HTTP請求的監聽與分發機制以及ASPNET運行時管道對HTTP請求的處理流程很多人留言為何沒有IIS 的介紹在寫作《WCF深入剖析》中為了剖析基於IIS的WCF服務寄宿(Hosting)再次對相關內容進行了研究在這裡一並與大家分享
IIS x與ASPNET
我們先來看看IIS x是如何處理基於ASPNET資源(比如aspxasmx等)請求的整個過程基本上可以通過圖體現
IIS x運行在進程InetInfoexe中在該進程中一個最重要的服務就是名為World Wide Web Publishing Service(簡稱WSVC)的Windows ServiceWSVC的主要功能包括HTTP請求的監聽工作進程的管理以及配置管理(通過從Metabase中加載相關配置信息)等
當檢測到某個HTTP Request後先根據擴展名判斷請求的是否是靜態資源(比limgtxtxml等)如果是則直接將文件內容以HTTP Response的形式返回如果是動態資源(比如aspxaspphp等等)則通過擴展名從IIS的腳本影射(Script Map)找到相應的ISAPI Dll
ISAPI是Internet服務器API(Internet Server Application Programming Interface)的縮寫是一套本地的(Native)Win API具有較高的執行性能是IIS和其他動態Web應用或者平台之間的紐帶比如ASP ISAPI橋接IIS與ASP而ASPNET ISAPI則連接著IIS與ASPNETISPAI定義在一個Dll中ASPNET ISAPI對應的Dll為Aspnet_isapidll你可以在目錄%windir%\MicrosoftNET\Framework\{version no}\中找到該Dll
ISAPI支持ISAPI擴展(ISAPI Extension)和ISAPI篩選(ISAPI Filter)前者是真正處理HTTP請求的接口後者則可以在HTTP請求真正被處理之前查看修改轉發或者拒絕請求比如IIS可以利用ISAPI篩選進行請求的驗證(Authentication)
如果我們請求的是一個基於ASPNET的資源類型比如aspx Web Page asmx Web Service或者svc WCF Service等Aspnet_isapidll會被加載ASPNET ISAPI擴展會創建ASPNET的工作進程(如果該進程尚未啟動)對於IIS x來說該工作進程為aspnetexeIIS進程與工作進程之間通過命名管道(Named Pipes)進程通信以獲得最好的性能
在工作進程初始化過程中NET 運行時(CLR)被加載從而構建了一個托管的環境對於某個Web應用的初次請求CLR會為其創建一個AppDomain在此AppDomain中HTTP運行時(HTTP Runtime)被加載並用以創建相應的應用對於寄宿於IIS x的所有Web 應用都運行在同一個進程(工作進程Aspnet_wpexe)的不同AppDomain中
IIS 與ASPNET
通過上面的介紹我們可以看出IIS x至少存在著如下兩個方面的不足
ISAPI Dll被加載到InetInfoexe進程中它和工作進程之間是一種典型的跨進程通信方式盡管采用性能最好的命名管道但是仍然會帶來性能的瓶頸 所有的ASPNET應用運行在相同的進程(aspnet_wpexe)中的不同的應用程序域(AppDomain)中基於應用程序域的隔離級別不能從根本上解決一個應用程序對另一個程序的影響在更多的時候我們需要不同的Web應用運行在不同的進程中 在IIS 中為了解決第一個問題ISAPIdll被直接加載到工作進程中為了解決第個問題引入了應用程序池(Application Pool)的機制我們可以為一個或者多個Web應用創建應用程序池每一個應用程序池對應一個獨立的工作進程從而為運行在不同應用程序池中的Web應用提供基於進程的隔離級別IIS 的工作進程名稱為wwpexe
當然除了上面兩點改進之外IIS 還有其他一些值得稱道的地方其中最重要的一點就是創建了一個新的HTTP監聽器HTTP協議棧(HTTP Protocol StackHTTPSYS)HTTPSYS運行在Windows的內核模式(Kernel Mode)下作為驅動程序而存在它是Windows 的TCP/IP網絡子系統的一部分從結構上它屬於TCP之上的一個網絡驅動程序嚴格地說HTTPSYS已經不屬於IIS的范疇了所以HTTPSYS的配置信息並不保存在IIS的元數據庫(Metabase)而是定義在注冊表中HTTPSYS的注冊表項位於下面的路徑中HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/HTTPHTTPSYS能夠帶來如下的好處
持續監聽由於HTTPSYS是一個網絡驅動程序始終處於運行狀態對於用戶的HTTP請求能夠及時作出反應 更好的穩定性HTTPSYS運行在操作系統內核模式下並不執行任何用戶代碼所以其本身不會受到Web應用工作進程和IIS進程的影響 內核模式下數據緩存如果某個資源被頻繁請求HTTPSYS會把響應的內容進行緩存緩存的內容可以直接響應後續的請求由於這是基於內核模式的緩存不存在內核模式和用戶模式的切換響應速度將得到極大的改進 圖體現了IIS的結構和處理HTTP請求的流程從中可以看出與IIS x不同WSVC從InetInfoexe進程脫離出來(對於IIS來說InetInfoexe基本上可以看作單純的IIS管理進程)運行在另一個進程SvcHostexe中不過WSVC的基本功能並沒有發生變化只是在功能的實現上作了相應的改進與IIS x一樣元數據庫(Metabase)依然存在於InetInfoexe進程中
當HTTPSYS監聽到用戶的HTTP請求後將其分發給WSVCWSVC解析出請求的URL並根據從Metabase獲取的URL與Web應用之間的映射關系得到目標應用並進一步得到目標應用運行的應用程序池或者工作進程如果工作進程不存在(尚未創建或者被回收)則為該請求創建新的工作進程工作進程的這種創建方式被稱為請求式創建在工作進程的初始化過程中相應的ISAPIdll被加載對於ASPNET應用來說被加載的ISAPIdll為Aspnet_ispaidllASPNET ISAPI再負責進行CLR的加載AppDomain創建Web Application的初始化等
IIS 與ASPNET
IIS 對請求的監聽和分發機制上又進行了革新性的改進主要體現在對於Windows進程激活服務(Windows Process Activation ServiceWAS)的引入將原來(IIS )WSVC承載的部分功能分流給了WAS具體來說通過上面的介紹我們知道對於IIS 來說WSVC主要承載著三大功能
HTTP請求接收接收HTTPSYS監聽到的HTTP請求 配置管理從元數據庫(Metabase)中加載配置信息對相關組件進行配置 進程管理創建回收監控工作進程 在IIS 後兩組功能被移入WAS中接收HTTP請求的任務依然落在WSVC頭上WAS的引入為IIS 一項前所未有的特性同時處理HTTP和非HTTP請求在WAS中通過一個重要的接口監聽器適配器接口(Listener Adapter Interface)抽象出不同協議監聽器監聽到的請求至於IIS下的監聽器除了基於網絡驅動的HTTPSYS提供HTTP請求監聽功能外WCF提供了種類型的監聽器TCP監聽器命名管道(Named Pipes)監聽器和MSMQ監聽器分別提供了基於TCP命名管道和MSMQ傳輸協議的監聽功能與此種監聽器相對的是種監聽器適配器(Adapter)提供監聽器與監聽器適配器接口之間的適配從這個意義上講IIS 中的WSVC更多地為HTTPSYS起著監聽適配器的功能WCF提供的這種監聽器和監聽適配器定義在程序集SMHostexe中你可以通過下面的目錄找到該程序集%windir%\MicrosoftNET\Framework\v\Windows Communication Foundatio
WCF提供的這種監聽器和監聽適配器最終以Windows Service的形式體現雖然它們定義在一個程序集中我們依然通過服務工作管理器(SCMService Control Manager)對其進行單獨的啟動終止和配置SMHostexe提供了個重要的Windows Service
NetTcpPortSharing為WCF提供TCP端口共享關於端口共享 NetTcpActivator為WAS提供基於TCP的激活請求包含TCP監聽器和對應的監聽適配器 NetPipeActivator為WAS提供基於命名管道的激活請求包含命名管道監聽器和對應的監聽適配器 NetMsmqActivator為WAS提供基於MSMQ的激活請求包含MSMQ監聽器和對應的監聽適配器 圖為上述的個Windows Service在服務控制管理器(SCM)中的呈現
圖 定義在SMHostexe中的Windows Service
圖揭示了IIS 的整體構架以及整個請求處理流程無論是從WSVC接收到的HTTP請求還是通過WCF提供的監聽適配器接收到的請求最終都會傳遞到WAS如果相應的工作進程(或者應用程序池)尚未創建其創建之否則將請求分發給對應的工作進程進行後續的處理WAS在進行請求處理過程中通過內置的配置管理模塊加載相關的配置信息對相關的組建進行配置與IIS x和IIS 基於Metabase的配置信息存儲不同的是IIS 大都將配置信息存放於XML形式的配置文件中基本的配置存放在fig中
圖 IIS 與ASPNET
ASPNET集成
從上面對IIS x和IIS 的介紹中我們不難發現這一點IIS與ASPNET是兩個相互獨立的管道(Pipeline)在各自管轄范圍內它們各自具有自己的一套機制對HTTP請求進行處理兩個管道通過ISAPI實現聯通IIS是第一道屏障當對HTTP請求進行必要的前期處理(比如身份驗證等)後通過ISAPI將請求分發給ASPNET管道當ASPNET在自身管道范圍內完成對HTTP請求的處理後處理後的結果再返回到IISIIS對其進行後期處理(比如日志記錄壓縮等)最終生成HTTP響應(HTTP Response)從另一個角度講IIS運行在非托管的環境中而ASPNET管道則是托管的從這個意義上講ISAPI還是連接非托管環境和托管環境的紐帶圖反映了IIS 與ASPNET之間的橋接關系
圖 基於IIS 與ASPNET雙管道設計
IIS x和IIS 下把兩個管道進行隔離至少帶來了下面一些局限與不足
相同操作的重復執行IIS與ASPNET之間具有一些重復的操作比如身份驗證 動態文件與靜態文件處理的不一致因為只有基於ASPNET的動態文件(比如aspxasmxsvc等等)的HTTP請求才能通過ASPNET ISAPI進入ASPNET管道而對於一些靜態文件(比lxmlimg等)的請求則由IIS直接響應那麼ASPNET管道中的一些功能將不能用於這些基於靜態文件的請求比如我們希望通過Forms認證應用於基於圖片文件的請求 IIS難以擴展對於IIS的擴展基本上就體現在自定義ISAPI但是對於大部分人來說這不是一件容易的事情因為ISAPI是基於Win的非托管的API並非一種面向應用的編程接口通常我們希望的是諸如定義ASPNET的HttpModule和HttpHandler一樣通過托管代碼的方式來擴展IIS 對於Windows平台下的IIS來講ASPNET無疑是一等公民它們之間不應該是井水不犯河水的關系而應該是你中有我我中有你的關系為此在IIS 中實現了兩者的集成對於集成模式下的IIS 我們獲得如下的好處
允許我們通過本地代碼(Native Code)和托管代碼(Managed Code)兩種方式定義IIS Module這些IIS Module注冊到IIS中形成一個通用的請求處理管道由這些IIS Module組成的這個管道能夠處理所有的請求不論請求基於怎樣的資源類型比如可以將FormsAuthenticationModule提供的Forms認證應用到基於aspxCGI和靜態文件的請求 將ASPNET提供的一些強大的功能應用到原來難以企及的地方比如將ASPNET的URL重寫功能置於身份驗證之前 采用相同的方式去實現配置檢測和支持一些服務器特性(Feature)比如ModuleHandler映射錯誤定制配置(Custom Error Configuration)等
圖 基於IIS 與ASPNET集成管道設計
圖演示了在ASPNET集成模式下IIS整個請求處理管道的結構我們可以看到原來ASPNET提供的托管組件可以直接應用在IIS管道中
ASPNET管道
以IIS 為例在工作進程wwpexe中利用Aspnet_ispaidll加載NET運行時(如果NET運行時尚未加載)IIS 引入了應用程序池的概念一個工作進程對應著一個應用程序池一個應用程序池可以承載一個或者多個Web應用每個Web應用映射到一個IIS虛擬目錄與IIS x一樣每一個Web應用運行在各自的應用程序域中
如果HTTPSYS接收到的HTTP請求是對該Web應用的第一次訪問當成功加載了運行時後會通過AppDomainFactory為該Web應用創建一個應用程序域(AppDomain)隨後一個特殊的運行時IsapiRuntime被加載IsapiRuntime定義在程序集SystemWeb中對應的命名空間為SystemWebHostingIsapiRuntime會接管該HTTP請求
IsapiRuntime會首先創建一個IsapiWorkerRequest對象用於封裝當前的HTTP請求並將該IsapiWorkerRequest對象傳遞給ASPNET運行時HttpRuntime從此時起HTTP請求正式進入了ASPNET管道根據IsapiWorkerRequest對象HttpRuntime會創建用於表示當前HTTP請求的上下文(Context)對象HttpContext
隨著HttpContext被成功創建HttpRuntime會利用HttpApplicationFactory創建新的或者獲取現有的HttpApplication對象實際上ASPNET維護著一個HttpApplication對象池HttpApplicationFactory從池中選取可用的HttpApplication用戶處理HTTP請求處理完畢後將其釋放到對象池中HttpApplicationFactory負責處理當前的HTTP請求
在HttpApplication初始化過程中會根據配置文件加載並初始化相應的HttpModule對象對於HttpApplication來說在它處理HTTP請求的不同的階段會觸發不同的事件(Event)而HttpModule的意義在於通過注冊HttpApplication的相應的事件將所需的操作注入整個HTTP請求的處理流程ASPNET的很多功能比如身份驗證授權緩存等都是通過相應的HttpModule實現的
而最終完成對HTTP請求的處理實現在另一個重要的對象中HttpHandler對於不同的資源類型具有不同的HttpHandler比如aspx頁對應的HttpHandler為SystemWebUIPageWCF的svc文件對應的HttpHandler為SystemServiceModelActivationHttpHandler上面整個處理流程如圖所示
圖 ASPNET 處理管道
HttpApplication
HttpApplication是整個ASPNET基礎架構的核心它負責處理分發給它的HTTP請求由於一個HttpApplication對象在某個時刻只能處理一個請求只有完成對某個請求的處理後HttpApplication才能用於後續的請求的處理所以ASPNET采用對象池的機制來創建或者獲取HttpApplication對象具體來講當第一個請求抵達的時候ASPNET會一次創建多個HttpApplication對象並將其置於池中選擇其中一個對象來處理該請求當處理完畢HttpApplication不會被回收而是釋放到池中對於後續的請求空閒的HttpApplication對象會從池中取出如果池中所有的HttpApplication對象都處於繁忙的狀態ASPNET會創建新的HttpApplication對象
HttpApplication處理請求的整個生命周期是一個相對復雜的過程在該過程的不同階段會觸發相應的事件我們可以注冊相應的事件將我們的處理邏輯注入到HttpApplication處理請求的某個階段
我們接下來介紹的HttpModule就是通過HttpApplication事件注冊的機制實現相應的功能的表按照實現的先後順利列出了HttpApplication在處理每一個請求時觸發的事件名稱
表 名稱
描述
BeginRequest
HTTP管道開始處理請求時會觸發BeginRequest事件
AuthenticateRequestPostAuthenticateRequest
ASPNET先後觸發這兩個事件使安全模塊對請求進行身份驗證
AuthorizeRequestPostAuthorizeRequest
ASPNET先後觸發這兩個事件使安全模塊對請求進程授權
ResolveRequestCachePostResolveRequestCache
ASPNET先後觸發這兩個事件以使緩存模塊利用緩存的直接對請求直接進程響應(緩存模塊可以將響應內容進程緩存對於後續的請求直接將緩存的內容返回從而提高響應能力)
PostMapRequestHandler
對於訪問不同的資源類型ASPNET具有不同的HttpHandler對其進程處理對於每個請求ASPNET會通過擴展名選擇匹配相應的HttpHandler類型成功匹配後該實現被觸發
AcquireRequestStatePostAcquireRequestState
ASPNET先後觸發這兩個事件使狀態管理模塊獲取基於當前請求相應的狀態比如SessionState
PreRequestHandlerExecutePostRequestHandlerExecute
ASPNET最終通過一請求資源類型相對應的HttpHandler實現對請求的處理在實行HttpHandler前後這兩個實現被先後觸發
ReleaseRequestStatePostReleaseRequestState
ASPNET先後觸發這兩個事件使狀態管理模塊釋放基於當前請求相應的狀態
UpdateRequestCachePostUpdateRequestCache
ASPNET先後觸發這兩個事件以使緩存模塊將HttpHandler處理請求得到的相應保存到輸出緩存中
LogRequestPostLogRequest
ASPNET先後觸發這兩個事件為當前請求進程日志記錄
EndRequest
整個請求處理完成後EndRequest事件被觸發
對於一個ASPNET應用來說HttpApplication派生於globalasax文件我們可以通過創建globalasax文件對HttpApplication的請求處理行為進行定制globalasax采用一種很直接的方式實現了這樣的功能這種方式既不是我們常用的方法重寫(Method Overriding)或者事件注冊而是直接采用方法名匹配在globalasax中我們按照這樣的方法命名規則進行事件注冊Application_{Event Name}比如Application_BeginRequest方法用於處理HttpApplication的BeginRequest事件如果通過VS創建一個globalasax文件下面是默認的定義
<%@ Application Language=C# %><script runat=server>void Application_Start(object sender EventArgs e) {}void Application_End(object sender EventArgs e) {}void Application_Error(object sender EventArgs e) {}void Session_Start(object sender EventArgs e) {}void Session_End(object sender EventArgs e) {}</script>HttpModule
ASPNET為創建各種NET Web應用提供了強大的平台它擁有一個具有高度可擴展性的引擎並且能夠處理對於不同資源類型的請求那麼是什麼成就了ASPNET的高可擴展性呢? HttpModule功不可沒
從功能上講HttpModule之於ASPNET就好比ISAPI Filter之於IIS一樣IIS將接收到的請求分發給相應的ISAPI Extension之前注冊的ISAPI Filter會先截獲該請求ISAPI Filter可以獲取甚至修改請求的內容完成一些額外的功能與之相似地當請求轉入ASPNET管道後最終負責處理該請求的是與請求資源類型相匹配的HttpHandler對象但是在Handler正式工作之前ASPNET會先加載並初始化所有配置的HttpModule對象HttpModule在初始化的過程中會將一些功能注冊到HttpApplication相應的事件中那麼在HttpApplication整個請求處理生命周期中的某個階段相應的事件會被觸發通過HttpModule注冊的事件處理程序也得以執行
所有的HttpModule都實現了IHttpModule接口下面是IHttpModule的定義其中Init方法用於實現HttpModule自身的初始化該方法接受一個HttpApplication對象有了這個對象事件注冊就很容易了
public interface IHttpModule{void Dispose();void Init(HttpApplication context);}ASPNET提供的很多基礎構件(Infrastructure)功能都是通過相應的HttpModule實現的下面類列出了一些典型的HttpModule:
OutputCacheModule實現了輸出緩存(Output Caching)的功能 SessionStateModule在無狀態的HTTP協議上實現了基於會話(Session)的狀態 WindowsAuthenticationModule + FormsAuthenticationModule + PassportAuthentication Module實現了種典型的身份認證方式Windows認證Forms認證和Passport認證 UrlAuthorizationModule + FileAuthorizationModule實現了基於Uri和文件ACL(Access Control List)的授權 而另外一個重要的HttpModule與WCF相關那麼就是SystemServiceModel ActivationHttpModuleHttpModule定義在SystemServiceModel程序集中在默認的情況下HttpModule完成了基於IIS的寄宿工作
除了這些系統定義的HttpModule之外我們還可以自定義HttpMoudle通過nfig我們可以很容易地將其注冊到我們的Web應用中
HttpHandler
如果說HttpModule相當於IIS的ISAPI Filter的話我們可以說HttpHandler則相當於IIS的ISAPI ExtensionHttpHandler在ASPNET中扮演請求的最終處理者的角色對於不同資源類型的請求ASPNET會加載不同的Handler來處理也就是說aspx page與asmx web service對應的Handler是不同的
所有的HttpHandler都實現了接口IHttpHandler下面是IHttpHandler的定義方法ProcessRequest提供了處理請求的實現
public interface IHttpHandler{void ProcessRequest(HttpContext context);bool IsReusable { get; }}對於某些HttpHandler具有一個與之相關的HttpHandlerFactory用於創建或者獲取相應的HttpHandlerHttpHandlerFactory實現接口IHttpHandlerFactory方法GetHandler用於創建新的HttpHandler或者獲取已經存在的HttpHandler
public interface IHttpHandlerFactory{IHttpHandler GetHandler(HttpContext context string requestType string url string pathTranslated);void ReleaseHandler(IHttpHandler handler);}HttpHandler和HttpHandlerFactory的類型都可以通過相同的方式配置到nfig中下面一段配置包含對種典型的資源類型的HttpHandler配置aspxasmx和svc可以看到基於WCF Service的HttpHandler類型為SystemServiceModelActivationHttpHandler
<?xml version= encoding=utf ?>
<configuration>
<systemweb>
<httpHandlers>
<add path=*svc verb=* type=SystemServiceModelActivationHttpHandler SystemServiceModel Version= Culture=neutral PublicKeyToken=bace validate=false/>
<add path=*aspx verb=* type=SystemWebUIPageHandlerFactory validate=True/>
<add path=*asmx verb=* type=SystemWebServicesProtocolsWebServiceHandlerFactory SystemWebServices Version= Culture=neutral PublicKeyToken=bfffdaa validate=False/>
</httpHandlers>
</systemweb>
</configuration>
From:http://tw.wingwit.com/Article/program/net/201311/12769.html