ASP.NET的底層體系1

引言:

學習ASP.NET,要想做的更好,不瞭解ASP.NET是不行的。一個有理想的程序員都會像擠壓海綿一樣,努力的去學習和獲取寶貴的知識。

一.ASP.NET是什麼

ASP.NET是一個請求處理引擎,它獲取客戶端的請求,然後通過內置的管道,把請求傳遞到終點,在這個終點,開發者可以添加處理這個請求的邏輯代碼。

整個ASP.NET引擎構建在託管代碼裡,所有的擴展功能都是通過託管代碼的擴展提供。

使用ASP.NET可以完成一些任務,之前這些任務是使用IIS的ISAPI擴展和過濾來完成的,儘管還有一些限制,但與ASP相比,已經有了很大的進步。ISAPI是底層Win32樣式的API,它的接口就有1M,這樣對於大型程序開發是非常困難的,由於ISAPI是底層的接口,因此它的速度也是很快的。

微軟的ASP.NET和IIS的接口是通過宿主在.NET裡的ISAPI擴展來通信的,ISAPI提供了與WebServer通信的核心接口,然後ASP.NET使用非託管代碼獲取請求發出響應。

二.HTTP

三.從瀏覽器到ASP.NET

我們從一個典型的ASP.NET Web請求的生命週期開始說起。用戶通過在瀏覽器輸入一個URL,點擊鏈接,提交一個HTML表單(一個POST請求),或者一個客戶端程序調用基於ASP.NET的Web Services。在服務端,IIS收到這個請求。

ASP.NET的底層通過ISAPI擴展與IIS通信,然後,通過ASP.NET這個請求通常被路由到一個帶.aspx擴展名的頁面,但是這個處理過程如何工作,則完全依賴於HTTP處理器(handler)的執行。在IIS中,.aspx經由“應用程序擴展”被映射到ASP.NET ISAPI的dll文件:aspnet_isapi.dll,每一個觸發ASP.NET的請求,都必須由一個已經註冊的,並且指向aspnet_isapi.dll的擴展名來標識。

依靠擴展名,ASP.NET把一個請求路由到一個恰當的處理器,該處理器則負責處理這個請求。

例子:Web Service的擴展名.asmx不會把一個請求路由到磁盤的某一個頁面,而是會路由到定義中附加了指定特性的類,此特性會把它標記為一個Web Services的實現。

四.ISAPI

ISAPI是底層非託管的Win32 API,它定義的接口非常的單一且性能最優。用這些接口處理原始指針(raw pointer),而函數指針列表(function pointer)則用於回調。ISAPI提供了最底層的、高性能的接口。

ISAPI趨向於被當做橋接口使用,用於給高層級的工具提供應用服務類型的功能。

例子:ASP.NET就是構建在ISAPI之上,ISAPI給高層次的應用程序提供了高性能垂直訪問接口,使得高層次的應用程序需要的信息可以從ISAPI提供的信息中提煉,引擎可以提煉ISAPI接口提供的表單裡的對象有:Request Response。

作為約定,ISAPI支持ISAPI擴展(extensions)和ISAPI過濾(filters),擴展是請求處理接口,提供了跟Web Server輸入和輸出相關的邏輯處理。從本質來說,它是一個事務接口。

ISAPI是鉤子接口,它允許你查看進入IIS的每一個請求並可以修改請求的內容。

ASP.NET的底層體系1

IIS把不同的擴展名如.aspx映射到ASP.NET的ISAPI擴展,通過這種機制,在WEB SERVER裡,請求就可以被路由到ASP.NET的處理管道里。

五.IIS5 IIS6

當一個請求進來的時候,IIS會檢查腳本映射,然後把請求路由到aspnet_isapi.dll。IIS 6總會保持一個單獨的工作進程:應用程序池,所有的處理都發生在這個進程裡。對於IIS 6來說,應用程序池是一個重大的改進,因為他們允許以更小的粒度控制一個指定進程的執行。你可以為每一個虛擬目錄或者整個web站點配置應用程序池,這樣它可以與運行在同一臺機器的其他程序完全隔離。

應用程序池是高度可配置的,通過設置應用程序池的執行許可,可以配置他們的執行環境。對於ASP.NET而言,IIS 6最大的改進是使用應用程序池代替了machine.config裡的ProcessModel實體的大部分功能。在IIS 5裡,這個實體是很難管理的,因為它的設置是全局的,而且不能夠在指定WEB程序的wen.config裡覆蓋這些設置。當IIS 6運行的時候,ProcessModel裡的大部分配置將被忽略,取而代之的是讀取應用程序池的配置。另外一些配置,像線程池的大小和IO線程數目等仍然還是要通過這個節點的配置。

IIS 6應用程序池也包含了ASP.NET固有的東西,ASP.NET可以和新的底層API通信,這些API允許直接訪問HTTP緩衝存儲器的API,而HTTP緩衝存儲器的API可以直接進入Web SERVER的緩衝存儲器,卸載ASP.NET級別的緩存。

在IIS 6裡,ISAPI擴展運行在應用程序池的工作進程裡,而.NET運行時也在這個進程裡,所以ISAPI擴展和.NET運行時通信是發生在進程內。

ASP.NET的底層體系1

六.進入.NET運行時

進入.NET運行時真正登錄點發生在一些沒有正式文檔的類和接口之間。工作進程w3wp.exe宿主在.NET運行裡。ISAPI dll通過底層的COM調用一小撮非託管類型的接口,其實最終調用的是ISAPIRuntime派生類的實例。進入運行時的第一個登錄點是未歸檔的ISAPIRuntime類,它通過COM把接口IISAPIRuntime暴露給調用者,這些COM接口是底層的IUnknown。基於這些接口,就意味著ISAPI到ASP.NET之間的調用屬於內部調用。

IISAPIRuntime接口擔當著來自ISAPI擴展的非託管代碼和託管代碼之間的橋樑

[return:MarshallAs(UnmanagerType.I4)]

int ProcessRequest(IntPtr ecb,[In,MarshallAs(UnmanagerType.I4)] int useProcessMode)

ecb參數是ISAPI擴展控制塊,它作為非託管資源傳遞給ProcessRequest方法,此方法將獲取ECB,然後作為基本的輸入和輸出接口,用於Request和Response對象。ISAPI ECB包含著所有底層的請求信息,這其中包括服務器變量、用於表單變量的輸入流和寫數據並把數據發送到客戶端的輸出流中。一個單獨的ECB引用基本提供了一個ISAPI請求可以訪問的所有功能。

ISAPI擴展以異步的方式處理請求,所以當ISAPI擴展調用了工作進程或者IIS的線程後,會立即返回,但會為當前的請求保留ECB,因此,ECB需要包含這樣的機制,當請求結束的時候通知ISAPI,然後ISAPI擴展釋放ECB資源。接著以異步的方式立即釋放ISAPI的工作線程和卸載由ASP.NET託管的那個隔離的處理線程。

ASP.NET得到ECB引用後,會在內部使用它來獲取當前請求的相關信息。如服務器變量。ECB將就存活直到這個請求結束或者IIS超時,在這之前,ASP.NET將會與ecb繼續保持通信,當請求結束的時候,輸出的內容會寫進ISAPI的輸出流中。然後ISAPI擴展會被通知請求已經結束,讓它知道ECB可以被釋放了,這個執行過程是非常高效的。

七.ProcessRequest

ISAPI是多線程的,因此請求可以以多線程的方式穿過AppDomainFactory.Create()返回的對象引用。

ASP.NET的底層體系1

ASP.NET的底層體系1

public int ProcessRequest(IntPtr ecb,int iWRType){ //ISAPIWorkerRequest從HttpWorkRequest繼承,這裡創建的是一個ISAPIWorkerRequest派生類的一個實例 HttpWorkerRequest request=ISAPIWorkerRequest.CreateWorkerRequest(ecb,iWRType); //得到請求的物理路徑 string str=request.GetAppPathTranslated(); //得到AppDomain的物理路徑 string str2=HttpRuntime.AppDomainAppPathInternal;  if( str2==null || str.Equals(".") || string.Compare(str,str2,true,CultureInfo.InvariantCulture)==0) { HttpRuntime.ProcessRequest(request); return 0; } //如果外部請求的AppDomain物理路徑和原來的AppDomain的路徑不同,說明ISAPI維持的AppDomain已經失效,所以需要把原來的城西關閉 HttpRuntime.ShutDownAppDomain("from "+str+str2); return 1;}
ASP.NET的底層體系1

上述代碼ProcessRequest接收一個ISAPI ecb對象和一個服務器類型參數,這個線程是安全的,因此多個ISAPI線程可以同時安全的調用單個返回對象的實例。

System.Web.Hosting.ISAPIWorkerRequest繼承抽象類HttpWorkerRequest,它的職責是創建一個抽象的輸入和輸出視圖,為Web程序的輸入提供服務。上面的代碼中有一個方法CreateWorkerRequest,這個方法的第二個參數用於指定創建什麼樣的工作請求對象,即ISAPIWorkerRequest的派生類,這裡有三個不同的版本:ISAPIWorkerRequestInProc,ISAPIWorkerRequestInProcForIIS6,ISAPIWorkerRequestOutOfProc。當這個請求到來時,這個ISAPIWorkerRequest將被創建,用於給Request和Response對象提供基礎服務,而這兩個對象將從數據的提供者WorkerRequest接收數據流。

抽象類HttpWworkerRequest圍繞著底層的接口提供了高層的抽象,這樣就不用考慮數據的來源,無論他是一個CGI Web Server,Web瀏覽器還是自定義的機制(用於把數據流入HTTP運行),ASP.NET都可以以同樣的方式從中獲取數據。

有關IIS的抽象主要集中在ISAPI ECB塊,在我們的請求處理當中,ISAPIWorkerRequest依賴於ISAPI ECB塊。

ISAPIWorkerRequest實現了一個高層次包裝器方法,它調用了低層次的核心方法,而這些方法負責實際調用非託管API或者是“服務層的實現”,核心的方法在ISAPIWorkerRequest的派生類中得以實現,這樣可以針對它宿主的環境提供特定的實現,為以後增加一個額外環境的實現類作為新的Web Server接口提供了便利。

說句實話,上面的內容講了這麼多,頭都被繞暈了。講來講去總結就是以下幾點:

1.用戶在瀏覽器輸入一個URL,提交一個HTML表單。服務端的IIS接收到請求,ASP.NET的底層ISAPI與IIS通信,路由到指定的頁面。

2.ISAPI是底層非託管的win32 API,ASP.NET都是構建在ISAPI之上,它給高層次的應用程序提供了高性能垂直訪問接口。

3.在ISAPI擴展裡,當第一個請求命中一個ASP.NET的映射擴展時,工作線程就會引導.NET運行時啟動。一旦運行時存在了,非託管代碼就可以為指定的虛擬目錄請求一個ISAPRuntime的實例,當然這個前提是這個實例還不存在,每個虛擬目錄都會擁有一個AppDomain,ISAPIRuntime存在於AppDomain中,它引導一個單獨的程序啟動。


分享到:


相關文章: