頁面的編譯
特定.aspx資源的程式集的產生分為兩個步驟進行。首先,該資源檔的原始碼會被解析,根據得到的資訊,從Page類(或Page的衍生類別)派生出相應的類。然後,動態產生的類會被編譯為程式集,該程式集之後會被緩衝到ASP.Net專用的臨時目錄下。
只要連結的aspx源檔案沒有被更改,且整個應用程式沒有重啟,已編譯的頁面就一直存在。對已連結aspx檔案的任何更改,將使相關程式集變為無效,並在該頁面下一次被請求時,強制HTTP運行庫建立新的程式集。
編輯web.config和global.asax之類的檔案會導致整個應用程式重啟。在這種情況下,在某個頁面被請求時,所有頁面會被重新編譯。如果bin檔案夾中程式集被改動(建立或替換),所有頁面也會被重新編譯。
運行機制
IIS的Web伺服器所提供的所有資源,會按副檔名進行組織。任何來訪的請求會被分配給特定的運行時進程模組進行實際處理。IIS上下文中可處理Web資源的模組是Internet伺服器應用程式編程介面(ISAPI)擴充--實際是傳統的Win32 dll。IIS和ISAPI擴充會針對專用的通訊協定使用這些dll入口方法。當IIS需要ISAPI擴充完成某項任務時,它會載入相應的dll,並通過有效參數調用適當的函數。
當某個資源請求到達時,IIS首先會判斷所請求資源類型。靜態資源直接由IIS處理,無需調用任何外部模組。IIS在本地Web伺服器上訪問被請求的檔案,並將其內容寫入輸出控制台返回給瀏覽器。資源地圖資訊儲存在IIS元庫中。ASP.Net在安裝時對IIS元庫進行修改,使aspnet_isapi.dll能處理某些典型的ASP.NET資源。列出了其中的一些:
IIS5進程模型
如果ASP.NET應用程式部署到Windows Server 2003前的系統中,只能選擇IIS5.0進程模型。在該模型中,aspnet_isapi.dll不處理aspx檔案,而是充當發送器。它收集有關被調URL和底層資源的所有資訊,將其發給另一個特殊進程aspnet_wp.exe。ISAPI擴充與背景工作處理序間的通訊是通過具名管道(named pipe)完成。單CPU下,背景工作處理序的一個副本始終運行,承托所有活動的Web應用程式;多個CPU下允許多個背景工作處理序運行,每個進程對應一個可用CPU。
每個Web應用程式是通過其虛擬目錄標識,分別從屬於獨立的應用程式定義域(AppDomain)。當某個虛擬目錄被用戶端第一次請求時,ASP.NET背景工作處理序會建立一個新的AppDomain。之後,ASP.NET運行庫載入所有所需的程式集,並將控制權交給託管HTTp管道,後者對該請求做實際的處理。
如果用戶端請求的是一個已在啟動並執行Web應用程式,ASP.NET運行庫只是將該請求轉寄給與其虛擬目錄相關聯的現有AppDomain。如果處理當前頁面的程式集沒被該AppDomain載入,則動態建立它;如在第一次調用時已被建立,則直接使用。
IIS6.0進程模型
如果Web伺服器作業系統是Windows Server 2003或更高版本,IIS6.0進程模型是ASP.NET的預設選擇。IIS6.0管道以一個名為w3wp.exe的背景工作處理序為中心。該可執行程式副本由分配給同一應用程式集區的所有Web應用程式共用。IIS6.0使我們能對應用程式集區進行定製,以達到託管於Web伺服器的各種應用程式所需的隔離程度。
w3wp.exe背景工作處理序會載入aspnet_isapi.dll,隨後,ISAPI擴充載入通用語言執行平台(CLR),啟動asp.net運行時管道,對請求進行處理。使用IIS6.0進程模型,asp.net內建的背景工作處理序便會被禁用。
IIS6.0以核心級模組形式實現了HTTP監聽程式。所有輸入請求會首先被一個驅動程式(http.sys)管理。http.sys驅動程式會監聽請求,將其追加到相應的應用程式集區請求隊列中。一個叫“Web管理服務”的模組會讀取IIS元庫,並指示http.sys驅動程式建立請求隊列,隊列數量與元庫中註冊的應用程式集區數量一致。
總之,使用IIS6.0進程模型,ASP.NET會運行得更快,因為inetinfo.exe(IIS管理服務)與背景工作處理序不需要進行任何處理序間通訊。HTTP請求直接投遞承托CLR的背景工作處理序。此外,ASP.NET背景工作處理序不是特殊的進程,而僅僅是IIS背景工作處理序的副本。這樣,回收進程、快取頁面面和監視健全狀態的負擔會由IIS承受。
被請求頁面的表示
每個引用aspx資源的輸入請求都會被映射到Page的衍生類別。Asp.net http運行時環境首先會確定處理該請求的類名。頁面URL與類名通過某種命名規範關聯在一起。如:請求頁面為default.aspx,則可推斷出相關聯類別名為ASP.default_aspx。如果當前載入到AppDomain的程式集不包含這個名稱的類,HTTP運行庫將發出該類的建立和編譯命令。該aspx資源的原始碼會被解析,以建立該類的原始碼,結果會臨時儲存在asp.net臨時檔案夾中。接下來,該類被編譯並載入到記憶體中,以處理該請求。當同一頁面的請求再次到達時,由於該類已存在,所以不會再執行編譯過程。
ASP.default_aspx類繼承於Page類或Page衍生類別。更確切的講,ASP.default_aspx的基類(由VS建立)會與程式碼後置類別(由ASP.NET HTTP運行庫動態組織)合并。
請求的處理
被請求的aspx類建立後,HTTP運行時環境通過公用介面IHttpHandler來調用該類。根類Page實現了該介面,它包含兩個成員ProcessRequest方法和布爾類型的IsReusable屬性。一旦HTTP運行庫獲得代表被請求資源類的執行個體,便調用ProcessRequest方法開始處理,以便向瀏覽器做出響應後終結。調用並執行ProcessRequest及其所觸發事件的整個過程稱為“頁面的生命週期”。
ASP.NET背景工作執行緒會將任何輸入請求交給HTTP管道。HTTP管道是一條完全可擴充的託管對象鏈,其工作方式與一般意義上的“管道”頗為相似。所有這些對象構成了所謂的ASP.NET HTTP運行時環境。
HttpRuntime對象
頁面請求會傳遞給管道中的每一個處理原始HTTP承載的對象,在該鏈路的終端產生要發給瀏覽器的標記代碼。HttpRuntime類就是該管道的進入點。對於輸入的每個請求,ASP.NET背景工作執行緒通過建立HttpRuntime類的執行個體,並調用其ProcessRequest來啟用HTTP管道。注意:儘管HttpRuntime.ProcessRequest與IHttpHandler介面的名稱意思相近,但二者實際並無關係。
HttpRuntime類包含許多私人和內部方法,但只公開了三個靜態方法:Close、ProcessRequest和UnloadAppDomain。
HttpRuntime對象會在建立時對許多輔助處理頁面請求的內部對象進行初始化。這些輔助對象包括緩衝管理器和檔案系統監視器(用於檢測構成應用程式的檔案的變動)。ProcessRequest方法被調用後,HttpRuntime對象即開始處理要發送到瀏覽器的頁面。它會為請求建立一個新的上下文,並初始化一個特殊的文本編寫器(writer)對象,該對象用於快取標籤代碼。內容物件是HttpContext類的執行個體,它封裝了所有與請求有關的HTTP特有的資訊。
之後,這個HttpRuntime對象使用上下文資訊尋找或建立能處理該請求的Web應用程式物件。通過包含在URL中虛擬目錄資訊,便可定位Web應用程式。尋找或建立應用程式的對象叫HttpApplicationFactory,這是一個內部使用的對象,負責返回能處理該請求的有效對象。
應用程式工廠
在應用程式的生存期中,HttpApplicationFactory對象維護著許多HttpApplication對象,該對象用於處理輸入的HTTP請求。當該程式工廠對象被調用後,它會驗證請求的目標虛擬資料夾是否存在。如果應用程式已運行,該工廠從可用的對象池中擷取一個HttpApplication對象,然後將它傳給請求。如果沒有可用的,則建立新的HttpApplication執行個體。
如果該虛擬目錄不曾被調用,則在新的AppDomain中針對該虛擬目錄建立一個HttpApplication對象。這樣,如果應用程式檔案global.asax存在,HttpApplication對象就需對它進行編譯,並建立代表實際被請求頁面的程式集。該過程相當於啟動應用程式。HttpApplication對象用於處理頁面請求,每次處理一個(多個對象用於處理並發的請求)。
HttpApplication對象
HttpApplication是一個基類,代表運行中的ASP.NET應用程式。運行中的ASP.NET應用程式由動態建立的繼承於HttpApplication的類來表示。如果global.asax存在,那麼通過解析其內容,可以建立動態產生的應用程式類的原始碼。如果global.asax可用,應用程式類便會被建立,並根據它命名為ASP.global_asax。否則,會使用基類HttpApplication。
HttpApplication衍生類別的執行個體負責管理分配給它的請求的整個生命週期。只有在該請求處理完畢後,該執行個體才會被重用。HttpApplication維護著一系列HTTP模組對象,這些對象可對請求的內容進行篩選,甚至還可進行修改。在請求穿越管道的過程中,可能隨時會調用登入的模組。
HttpApplication對象能判斷代表被請求資源的物件類型,隨後,HttpApplication使用相應的處理常式工廠擷取代表被請求資源的對象。工廠可能使用現有的程式集執行個體化被請求資源的類執行個體,也可能動態建立所需程式集,然後再執行個體化該對象。處理常式工廠對象實現了IHttpHandlerFactory介面,負責返回處理Http請求的託管對象--HTTP處理常式。一個ASP.NET頁面只是一個處理常式對象(即實現IHttpHandler介面的類執行個體)。
頁面工廠
一旦HttpApplication對象掌管了請求,就必須選擇的個合適的處理常式,並建立該處理常式的執行個體。對於面向頁面的請求,對應的工廠名為PageHandlerFactory。為找到合適的處理常式,HttpApplication會讀取設定檔<httpHandlers>區段中的資訊。下表包含了幾個主要的登入處理常式:
處理常式工廠不會在每次調用被請求資源時都進行編譯,已編譯代碼被儲存在Web伺服器的ASP.NET臨時目錄中。
接到請求時,頁面處理工廠會建立代表被請求頁面的對象執行個體。頁面對象繼承於System.Web.UI.Page類,該類實現了IHttpHandler介面。頁面對象會被返回給應用程式工廠,隨後被傳給HttpRuntime對象。最後的步驟由ASP.NET運行庫完成,ASP.NET運行庫會調用IHttpHandler的頁面對象的ProcessRequest方法。這會使頁面執行使用者定義的代碼,並為瀏覽器產生標記。
總結
HTTP頁面請求處理流程如:
頁面的編譯
特定.aspx資源的程式集的產生分為兩個步驟進行。首先,該資源檔的原始碼會被解析,根據得到的資訊,從Page類(或Page的衍生類別)派生出相應的類。然後,動態產生的類會被編譯為程式集,該程式集之後會被緩衝到ASP.Net專用的臨時目錄下。
只要連結的aspx源檔案沒有被更改,且整個應用程式沒有重啟,已編譯的頁面就一直存在。對已連結aspx檔案的任何更改,將使相關程式集變為無效,並在該頁面下一次被請求時,強制HTTP運行庫建立新的程式集。
編輯web.config和global.asax之類的檔案會導致整個應用程式重啟。在這種情況下,在某個頁面被請求時,所有頁面會被重新編譯。如果bin檔案夾中程式集被改動(建立或替換),所有頁面也會被重新編譯。
運行機制
IIS的Web伺服器所提供的所有資源,會按副檔名進行組織。任何來訪的請求會被分配給特定的運行時進程模組進行實際處理。IIS上下文中可處理Web資源的模組是Internet伺服器應用程式編程介面(ISAPI)擴充--實際是傳統的Win32 dll。IIS和ISAPI擴充會針對專用的通訊協定使用這些dll入口方法。當IIS需要ISAPI擴充完成某項任務時,它會載入相應的dll,並通過有效參數調用適當的函數。
當某個資源請求到達時,IIS首先會判斷所請求資源類型。靜態資源直接由IIS處理,無需調用任何外部模組。IIS在本地Web伺服器上訪問被請求的檔案,並將其內容寫入輸出控制台返回給瀏覽器。資源地圖資訊儲存在IIS元庫中。ASP.Net在安裝時對IIS元庫進行修改,使aspnet_isapi.dll能處理某些典型的ASP.NET資源。列出了其中的一些:
IIS5進程模型
如果ASP.NET應用程式部署到Windows Server 2003前的系統中,只能選擇IIS5.0進程模型。在該模型中,aspnet_isapi.dll不處理aspx檔案,而是充當發送器。它收集有關被調URL和底層資源的所有資訊,將其發給另一個特殊進程aspnet_wp.exe。ISAPI擴充與背景工作處理序間的通訊是通過具名管道(named pipe)完成。單CPU下,背景工作處理序的一個副本始終運行,承托所有活動的Web應用程式;多個CPU下允許多個背景工作處理序運行,每個進程對應一個可用CPU。
每個Web應用程式是通過其虛擬目錄標識,分別從屬於獨立的應用程式定義域(AppDomain)。當某個虛擬目錄被用戶端第一次請求時,ASP.NET背景工作處理序會建立一個新的AppDomain。之後,ASP.NET運行庫載入所有所需的程式集,並將控制權交給託管HTTp管道,後者對該請求做實際的處理。
如果用戶端請求的是一個已在啟動並執行Web應用程式,ASP.NET運行庫只是將該請求轉寄給與其虛擬目錄相關聯的現有AppDomain。如果處理當前頁面的程式集沒被該AppDomain載入,則動態建立它;如在第一次調用時已被建立,則直接使用。
IIS6.0進程模型
如果Web伺服器作業系統是Windows Server 2003或更高版本,IIS6.0進程模型是ASP.NET的預設選擇。IIS6.0管道以一個名為w3wp.exe的背景工作處理序為中心。該可執行程式副本由分配給同一應用程式集區的所有Web應用程式共用。IIS6.0使我們能對應用程式集區進行定製,以達到託管於Web伺服器的各種應用程式所需的隔離程度。
w3wp.exe背景工作處理序會載入aspnet_isapi.dll,隨後,ISAPI擴充載入通用語言執行平台(CLR),啟動asp.net運行時管道,對請求進行處理。使用IIS6.0進程模型,asp.net內建的背景工作處理序便會被禁用。
IIS6.0以核心級模組形式實現了HTTP監聽程式。所有輸入請求會首先被一個驅動程式(http.sys)管理。http.sys驅動程式會監聽請求,將其追加到相應的應用程式集區請求隊列中。一個叫“Web管理服務”的模組會讀取IIS元庫,並指示http.sys驅動程式建立請求隊列,隊列數量與元庫中註冊的應用程式集區數量一致。
總之,使用IIS6.0進程模型,ASP.NET會運行得更快,因為inetinfo.exe(IIS管理服務)與背景工作處理序不需要進行任何處理序間通訊。HTTP請求直接投遞承托CLR的背景工作處理序。此外,ASP.NET背景工作處理序不是特殊的進程,而僅僅是IIS背景工作處理序的副本。這樣,回收進程、快取頁面面和監視健全狀態的負擔會由IIS承受。
被請求頁面的表示
每個引用aspx資源的輸入請求都會被映射到Page的衍生類別。Asp.net http運行時環境首先會確定處理該請求的類名。頁面URL與類名通過某種命名規範關聯在一起。如:請求頁面為default.aspx,則可推斷出相關聯類別名為ASP.default_aspx。如果當前載入到AppDomain的程式集不包含這個名稱的類,HTTP運行庫將發出該類的建立和編譯命令。該aspx資源的原始碼會被解析,以建立該類的原始碼,結果會臨時儲存在asp.net臨時檔案夾中。接下來,該類被編譯並載入到記憶體中,以處理該請求。當同一頁面的請求再次到達時,由於該類已存在,所以不會再執行編譯過程。
ASP.default_aspx類繼承於Page類或Page衍生類別。更確切的講,ASP.default_aspx的基類(由VS建立)會與程式碼後置類別(由ASP.NET HTTP運行庫動態組織)合并。
請求的處理
被請求的aspx類建立後,HTTP運行時環境通過公用介面IHttpHandler來調用該類。根類Page實現了該介面,它包含兩個成員ProcessRequest方法和布爾類型的IsReusable屬性。一旦HTTP運行庫獲得代表被請求資源類的執行個體,便調用ProcessRequest方法開始處理,以便向瀏覽器做出響應後終結。調用並執行ProcessRequest及其所觸發事件的整個過程稱為“頁面的生命週期”。
ASP.NET背景工作執行緒會將任何輸入請求交給HTTP管道。HTTP管道是一條完全可擴充的託管對象鏈,其工作方式與一般意義上的“管道”頗為相似。所有這些對象構成了所謂的ASP.NET HTTP運行時環境。
HttpRuntime對象
頁面請求會傳遞給管道中的每一個處理原始HTTP承載的對象,在該鏈路的終端產生要發給瀏覽器的標記代碼。HttpRuntime類就是該管道的進入點。對於輸入的每個請求,ASP.NET背景工作執行緒通過建立HttpRuntime類的執行個體,並調用其ProcessRequest來啟用HTTP管道。注意:儘管HttpRuntime.ProcessRequest與IHttpHandler介面的名稱意思相近,但二者實際並無關係。
HttpRuntime類包含許多私人和內部方法,但只公開了三個靜態方法:Close、ProcessRequest和UnloadAppDomain。
HttpRuntime對象會在建立時對許多輔助處理頁面請求的內部對象進行初始化。這些輔助對象包括緩衝管理器和檔案系統監視器(用於檢測構成應用程式的檔案的變動)。ProcessRequest方法被調用後,HttpRuntime對象即開始處理要發送到瀏覽器的頁面。它會為請求建立一個新的上下文,並初始化一個特殊的文本編寫器(writer)對象,該對象用於快取標籤代碼。內容物件是HttpContext類的執行個體,它封裝了所有與請求有關的HTTP特有的資訊。
之後,這個HttpRuntime對象使用上下文資訊尋找或建立能處理該請求的Web應用程式物件。通過包含在URL中虛擬目錄資訊,便可定位Web應用程式。尋找或建立應用程式的對象叫HttpApplicationFactory,這是一個內部使用的對象,負責返回能處理該請求的有效對象。
應用程式工廠
在應用程式的生存期中,HttpApplicationFactory對象維護著許多HttpApplication對象,該對象用於處理輸入的HTTP請求。當該程式工廠對象被調用後,它會驗證請求的目標虛擬資料夾是否存在。如果應用程式已運行,該工廠從可用的對象池中擷取一個HttpApplication對象,然後將它傳給請求。如果沒有可用的,則建立新的HttpApplication執行個體。
如果該虛擬目錄不曾被調用,則在新的AppDomain中針對該虛擬目錄建立一個HttpApplication對象。這樣,如果應用程式檔案global.asax存在,HttpApplication對象就需對它進行編譯,並建立代表實際被請求頁面的程式集。該過程相當於啟動應用程式。HttpApplication對象用於處理頁面請求,每次處理一個(多個對象用於處理並發的請求)。
HttpApplication對象
HttpApplication是一個基類,代表運行中的ASP.NET應用程式。運行中的ASP.NET應用程式由動態建立的繼承於HttpApplication的類來表示。如果global.asax存在,那麼通過解析其內容,可以建立動態產生的應用程式類的原始碼。如果global.asax可用,應用程式類便會被建立,並根據它命名為ASP.global_asax。否則,會使用基類HttpApplication。
HttpApplication衍生類別的執行個體負責管理分配給它的請求的整個生命週期。只有在該請求處理完畢後,該執行個體才會被重用。HttpApplication維護著一系列HTTP模組對象,這些對象可對請求的內容進行篩選,甚至還可進行修改。在請求穿越管道的過程中,可能隨時會調用登入的模組。
HttpApplication對象能判斷代表被請求資源的物件類型,隨後,HttpApplication使用相應的處理常式工廠擷取代表被請求資源的對象。工廠可能使用現有的程式集執行個體化被請求資源的類執行個體,也可能動態建立所需程式集,然後再執行個體化該對象。處理常式工廠對象實現了IHttpHandlerFactory介面,負責返回處理Http請求的託管對象--HTTP處理常式。一個ASP.NET頁面只是一個處理常式對象(即實現IHttpHandler介面的類執行個體)。
頁面工廠
一旦HttpApplication對象掌管了請求,就必須選擇的個合適的處理常式,並建立該處理常式的執行個體。對於面向頁面的請求,對應的工廠名為PageHandlerFactory。為找到合適的處理常式,HttpApplication會讀取設定檔<httpHandlers>區段中的資訊。下表包含了幾個主要的登入處理常式:
處理常式工廠不會在每次調用被請求資源時都進行編譯,已編譯代碼被儲存在Web伺服器的ASP.NET臨時目錄中。
接到請求時,頁面處理工廠會建立代表被請求頁面的對象執行個體。頁面對象繼承於System.Web.UI.Page類,該類實現了IHttpHandler介面。頁面對象會被返回給應用程式工廠,隨後被傳給HttpRuntime對象。最後的步驟由ASP.NET運行庫完成,ASP.NET運行庫會調用IHttpHandler的頁面對象的ProcessRequest方法。這會使頁面執行使用者定義的代碼,並為瀏覽器產生標記。
總結
HTTP頁面請求處理流程如: