天天看點

艾偉:[原創]再談IIS與ASP.NET管道

在2007年9月份,我曾經寫了三篇詳細介紹IIS架構和ASP.NET運作時管道的文章,深入介紹了IIS 5.x與IIS 6.0HTTP請求的監聽與分發機制,以及ASP.NET運作時管道對HTTP請求的處理流程:

<a href="http://www.cnblogs.com/artech/archive/2007/09/09/887528.html">[原創]ASP.NET Process Model之一:IIS 和 ASP.NET ISAPI</a>

<a href="http://www.cnblogs.com/artech/archive/2007/09/13/891262.html">[原創]ASP.NET Process Model之二:ASP.NET Http Runtime Pipeline - Part I</a>

<a href="http://www.cnblogs.com/artech/archive/2007/09/13/891266.html">[原創]ASP.NET Process Model之二:ASP.NET Http Runtime Pipeline - Part II</a>

很多人留言為何沒有IIS 7的介紹。在寫作《WCF深入剖析》中,為了剖析基于IIS的WCF服務寄宿(Hosting),再次對相關内容進行了研究,在這裡一并與大家分享。

IIS 5.x與ASP.NET

我們先來看看IIS 5.x是如何處理基于ASP.NET資源(比如.aspx,.asmx等)請求的,整個過程基本上可以通過圖1展現。

IIS 5.x運作在程序InetInfo.exe中,在該程序中一個最重要的服務就是名為World Wide Web Publishing Service(簡稱W3SVC)的Windows Service。W3SVC的主要功能包括HTTP請求的監聽、工作程序的管理以及配置管理(通過從Metabase中加載相關配置資訊)等。

當檢測到某個HTTP Request後,先根據擴充名判斷請求的是否是靜态資源(比如.html,.img,.txt,.xml等),如果是則直接将檔案内容以HTTP Response的形式傳回。如果是動态資源(比如.aspx,asp,php等等),則通過擴充名從IIS的腳本影射(Script Map)找到相應的ISAPI Dll。

<a href="http://images.cnblogs.com/cnblogs_com/artech/WindowsLiveWriter/IISASP.NET_457/clip_image002_2.jpg"></a>

艾偉:[原創]再談IIS與ASP.NET管道

 圖1 IIS 5.x與ASP.NET 

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中。

IIS 6與ASP.NET

通過上面的介紹,我們可以看出IIS 5.x至少存在着如下兩個方面的不足:

ISAPI Dll被加載到InetInfo.exe程序中,它和工作程序之間是一種典型的跨程序通信方式,盡管采用性能最好的命名管道,但是仍然會帶來性能的瓶頸;

所有的ASP.NET應用,運作在相同的程序(aspnet_wp.exe)中的不同的應用程式域(AppDomain)中,基于應用程式域的隔離級别不能從根本上解決一個應用程式對另一個程式的影響,在更多的時候,我們需要不同的Web應用運作在不同的程序中。

在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程序中。

<a href="http://images.cnblogs.com/cnblogs_com/artech/WindowsLiveWriter/IISASP.NET_457/clip_image004_2.jpg"></a>

艾偉:[原創]再談IIS與ASP.NET管道

 圖2 IIS 6與ASP.NET

當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 7.0與ASP.NET

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監聽器和對應的監聽擴充卡。

圖3為上述的4個Windows Service在服務控制管理器(SCM)中的呈現。

<a href="http://images.cnblogs.com/cnblogs_com/artech/WindowsLiveWriter/IISASP.NET_457/clip_image006_2.jpg"></a>

艾偉:[原創]再談IIS與ASP.NET管道

圖3 定義在SMHost.exe中的Windows Service

圖4揭示了IIS 7.0的整體構架以及整個請求處理流程。無論是從W3SVC接收到的HTTP請求,還是通過WCF提供的監聽擴充卡接收到的請求,最終都會傳遞到WAS。如果相應的工作程序(或者應用程式池)尚未建立,其建立之;否則将請求分發給對應的工作程序進行後續的處理。WAS在進行請求處理過程中,通過内置的配置管理子產品加載相關的配置資訊對相關的組建進行配置,與IIS 5.x和IIS 6.0基于Metabase的配置資訊存儲不同的是,IIS 7.0大都将配置資訊存放于XML形式的配置檔案中。基本的配置存放在applicationHost.cofig中。

<a href="http://images.cnblogs.com/cnblogs_com/artech/WindowsLiveWriter/IISASP.NET_457/clip_image008_2.jpg"></a>

艾偉:[原創]再談IIS與ASP.NET管道

圖4 IIS 7與ASP.NET

ASP.NET內建

從上面對IIS 5.x和IIS 6.0的介紹中,我們不難發現這一點,IIS與ASP.NET是兩個互相獨立的管道(Pipeline),在各自管轄範圍内,它們各自具有自己的一套機制對HTTP請求進行處理。兩個管道通過ISAPI實作“聯通”:IIS是第一道屏障,當對HTTP請求進行必要的前期處理(比如身份驗證等)後,通過ISAPI将請求分發給ASP.NET管道。當ASP.NET在自身管道範圍内完成對HTTP請求的處理後,處理後的結果再傳回到IIS,IIS對其進行後期處理(比如日志記錄、壓縮等),最終生成HTTP響應(HTTP Response)。從另一個角度講,IIS運作在非托管的環境中,而ASP.NET管道則是托管的,從這個意義上講,ISAPI還是連接配接非托管環境和托管環境的紐帶。圖5反映了IIS 6.0與ASP.NET之間的橋接關系。

<a href="http://images.cnblogs.com/cnblogs_com/artech/WindowsLiveWriter/IISASP.NET_457/clip_image012_2.jpg"></a>

艾偉:[原創]再談IIS與ASP.NET管道

圖5 基于IIS 6.0與ASP.NET雙管道設計

IIS 5.x和IIS 6.0下把兩個管道進行隔離至少帶來了下面一些局限與不足:

相同操作的重複執行:IIS與ASP.NET之間具有一些重複的操作,比如身份驗證;

動态檔案與靜态檔案處理的不一緻:因為隻有基于ASP.NET的動态檔案(比如.aspx、.asmx、.svc等等)的HTTP請求才能通過ASP.NET ISAPI進入ASP.NET管道,而對于一些靜态檔案(比如.html、.xml、.img等)的請求,則由IIS直接響應,那麼ASP.NET管道中的一些功能将不能用于這些基于靜态檔案的請求,比如,我們希望通過Forms認證應用于基于圖檔檔案的請求;

IIS難以擴充:對于IIS的擴充基本上就展現在自定義ISAPI,但是對于大部分人來說,這不是一件容易的事情。因為ISAPI是基于Win32的非托管的API,并非一種面向應用的程式設計接口。通常我們希望的是諸如定義ASP.NET的HttpModule和HttpHandler一樣,通過托管代碼的方式來擴充IIS。

對于Windows平台下的IIS來講,ASP.NET無疑是一等公民,它們之間不應該是“井水不犯河水”的關系,而應該是“你中有我,我中有你”的關系。為此,在IIS 7.0中,實作了兩者的內建。對于內建模式下的IIS 7.0,我們獲得如下的好處。

允許我們通過本地代碼(Native Code)和托管代碼(Managed Code)兩種方式定義IIS Module,這些IIS Module注冊到IIS中形成一個通用的請求處理管道。由這些IIS Module組成的這個管道能夠處理所有的請求,不論請求基于怎樣的資源類型。比如,可以将FormsAuthenticationModule提供的Forms認證應用到基于.aspx,CGI和靜态檔案的請求。

将ASP.NET提供的一些強大的功能應用到原來難以企及的地方,比如将ASP.NET的URL重寫功能置于身份驗證之前;

采用相同的方式去實作、配置、檢測和支援一些伺服器特性(Feature),比如Module、Handler映射、錯誤定制配置(Custom Error Configuration)等。

<a href="http://images.cnblogs.com/cnblogs_com/artech/WindowsLiveWriter/IISASP.NET_457/clip_image014_2.jpg"></a>

艾偉:[原創]再談IIS與ASP.NET管道

圖6 基于IIS 7.0與ASP.NET內建管道設計

圖6示範了在ASP.NET內建模式下,IIS整個請求處理管道的結構。我們可以看到,原來ASP.NET提供的托管元件可以直接應用在IIS管道中。

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。上面整個處理流程如圖7所示。

<a href="http://images.cnblogs.com/cnblogs_com/artech/WindowsLiveWriter/IISASP.NET_457/clip_image016_2.jpg"></a>

艾偉:[原創]再談IIS與ASP.NET管道

圖7 ASP.NET 處理管道

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在處理每一個請求時觸發的事件名稱。

表1

名稱

描述

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檔案,下面是預設的定義。

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對象,有了這個對象,事件注冊就很容易了。

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提供了處理請求的實作。

對于某些HttpHandler,具有一個與之相關的HttpHandlerFactory,用于建立或者擷取相應的HttpHandler。HttpHandlerFactory實作接口IHttpHandlerFactory,方法GetHandler用于建立新的HttpHandler,或者擷取已經存在的HttpHandler。

HttpHandler和HttpHandlerFactory的類型都可以通過相同的方式配置到Web.config中。下面一段配置包含對3種典型的資源類型的HttpHandler配置:.aspx,.asmx和.svc。可以看到基于WCF Service的HttpHandler類型為:System.ServiceModel.Activation.HttpHandler。

繼續閱讀