ASP.NET 執行原理

ForTechnology發表於2011-08-17

一個ASP.NET的應用程式是開始於IIS.

當你請求一個包含ASP.NET應用的網址時,IIS接受到請求(IISWEB服務守候程式),IIS收到請求後,會根據請求者請求的主機頭或者IP或者埠號來找到對應的站點.

 

當找到站點後,如果你請求的資源是以ASPX為結尾的WEBFORM,,IIS會將控制權交給一個ISAPI擴充套件.,名叫 AspNet_ISAIP.DLL.這時,控制權由IIS交到ASPNETISAPI擴充套件上.,需要說明的是,ISAPI擴充套件的級別低於IIS,但高於 使用者站點,它獨立於站點之外

 

ISAPI收到處理請求後,會啟動一個ASP.NET工作程式.然後將請求者的請求資訊轉交給ASP.NET工作程式(名為 ASPNET_WP.EXE).接下來,控制權由ASPNET_WP掌握.ASPNET_WP首先解出請求者的資訊,如果請求者請求的ASP.NET應用 程式(站點或虛擬目錄,通俗一點)尚未擁有APPDOMAIN,ASPNET_WP就會建立一個APPDOMAIN,並且將被請求的ASP.NET應用所 需的Assembly(就是那些DLL,例如System.Web.DLL)載入到APPDOMAIN

 

以上的步驟可以看到一個結論和規律:控制權是以流水式在各個請求處理者間傳遞,並且,前一個處理請求者必須負責傳遞後一個處理請求者所需的資訊.而且要負責裝載或初始化後一個處理者,這很像我們生活中的接力賽.

 

問題是,可能有許多人會問:幹嘛要如此繁瑣呢?直接由IIS把請求轉交給ASPNET_WP如何呢?不是不可以,而是如此一來,這個處理過程的可擴充套件性就變低了.ASPNET ISAPIIISASPNET_WP之間的橋樑.雖然看起來它僅僅負責轉交請求等工作.可是這樣一來,就大大擴充套件延展性.

 

另外一個疑問是關於APPDOMAIN,包括我,對於APPDOMAIN一開始的理解就曾陷入誤區,APPDOMAIN這東東微軟講的也比較 含糊,有人說跟程式一樣,但我一開始理解成了IIS裡的應用程式池,所以,走了不少彎路,實際上,APPDOMAIN既不是程式,也不是IIS裡的應用程 序池概念..NET下的所有應用程式都執行於APPDOMAIN之中(我自己的理解),每一個APPDOMAIN是一個執行的容器,每執行一個應用程式或者ASP.NET應用,.NET執行環境就會建立一個APPDOMAIN,然後把應用程式需要的一些DLL載入.APPDOMAIN的功能很像程式,但絕不是程式.你可以這樣理解,APPDOMAIN就是ASP.NET應用程式的執行環境吧.

 

AspNet_WP不光負責建立APPDOMAIN(當然,如果已經存在的話,就直接使用這個DOMAIN),另外,它在APPDOMAIN 建立後,還會將請求轉發至對應的APPDOMAIN中的ISAPIRuntime物件(Isapiruntime物件是APPDOMAIN的一部分)ISAPIRUNTIME專門負責解出請求的必要信息。它將資訊和請求轉交給HttpRuntime。在這裡,需要說明的是IsapiRuntime是一個類,它的全稱是System.Web.Hosting.ISAPIRuntime,而HttpRuntime也是一個類,它的全稱是 System.Web.HttpRuntime。因此,可以說,這兩個物件是APPDOMAIN執行環境的一部分,在ASPNET_WP建立 APPDOMAIN的同時,也會作為執行環境來建立這兩個物件.

 

由於接二連三的講述了幾個物件,所以,當我第一遍看這本書特別是看到這部分時,覺得特別暈,因為第一對.NET FRAMEWORK的類庫不甚瞭解,第二,ASP.NET的執行原理初次接觸.摸不著頭腦,總想把這些物件名稱與某個DLL或者某個實際上的檔案來對 應.其實不然,不管是ISAPIRuntime也好,還是HttpRuntime,它們在APPDOMAIN建立時,作為APPDOMAIN的一部分被實 例化.所以它們代表的是記憶體中的一個類的例項,也就是物件.並且,這上面的一部分運作原理,似乎跟ASP.NET應用程式沒有直接聯絡.似乎不入正題,很 容易讓初看者不知所云.實際上,可以說,IISISAPI是完成了請求的第一個部,也就是接納客戶請求.ISAPIAPPDOMAIN,是第二部分,也就是初始化部分,旨在建立處理請求的大環境,為下面處理請求和執行ASP.NET應用程式作好準備.

 

接下來,APPDOMAIN初始化完成後,接下來就需要建立會話了吧,因此,請求由HttpRuntime來接受,HttpRunTime主 要的工作便是為每一個提出請求的客戶建立一個HttpContext物件.這個東東又管理著HttpSession物件.每一個訪問者有各自的 HttpContext物件和HttpSession物件,這些物件,你可以在.NET FRAMEWORK庫中找到對應的類名,System.Web.HttpContext,System.Web.HttpSessionState.

 

可以看出,請求的處理過程非常類似於.NET中事件模型的處理過程.若干個處理模組被串接到一個事件上.ASP.NET執行原理裡,也是,若干個模組依次輪流處理一個請求,像流水線操作一樣.

 

另外,作為元件開發者,還要明確一個HttpRuntime,HttpContext,HttpSession這些物件的層次關係和呼叫建立關係.細節部分無需瞭解,只要知道誰建立了誰,誰被誰呼叫即可

 

HttpRuntime負責建立HttpContextHttpSession,httpContext負責管理httpSession

 

 

HttpRuntime建立完httpContext為止,實際上,你的應用程式仍然沒有執行,或者說,請求者的請求實際上並未真正的被處理,前面的工作都是些準備性或者輔助性的工作.

 

HttpRuntime除了建立上面的物件外,還要建立HttpApplication.至於建立Application物件的過程,是比較複雜的.你可以把其作為一個分支流程涉略一下

 

 

 

接下來,HttpApplication呼叫ProcessRequest方法來處理使用者請求,此方法會呼叫對應的HttpHandler來處理使用者請求,HttpHandler根據使用者請求的檔案的副檔名處理請求,並把請求的結果,也就是HTML傳送到客戶瀏覽器

 

另外,過程的複雜性遠遠超出了上面的描述,基本上,黃先生這本書的第三章第一節用了十幾頁文字在描述ASP.NET執行過程及原理,以及處理請 求時用的一些手法,但總體上的過程如上面的描述那樣,只不過,我沒有將建立各種物件時的細節剝離出來展示給大家.黃先生原著上的這節內容實際上非常詳細. 但為何大家看起來均言吃力呢?一方面是因為原理部分一向比較麻煩,另外一方面,是因為黃先生在講述時,沒有先向大家概要的描述過程和綱領,然後再描述細 節,再是直接把細節和綱領融合在一起.這樣,如果看的時候,沒有去將這節的各個小標題和內容串聯起來並先總結出綱領來的話.看完後,就會頭暈.實際上,整 個講述就是在描述ASP.NET處理請求的過程.如果隱藏所有技術性的細節,而只講流程的話,大家可能很快理解.然後再將流程中的每一部分的技術細節展現 出來,我想,容易理解的多.這好比講故事,先將故事梗概說一下比較好吧.

 

當然,我不是說黃先生寫的不好,實際上,這一節寫的很透徹,看懂了,就很受用.流程是很重要的,它的重要性在於你知道了在何時發生何事,就可以在指定的時間點做一些處理.這一點,在黃先生本書以後的章節中講述ASP.NET PAGE物件執行流程中更顯重要.

 

下面的圖對整個ASP.NET應用執行過程中的各個物件的職能以及流程做了圖解.當然,圖解拋棄了技術性的細節,例如,HttpApplication如何建立等 
 

 

 

上一篇隨筆<深入剖析ASP.NET元件設計>一書中第三章關於ASP.NET執行原理的補白總覽了大體的結構及流程,看完後,相信,可以對整體的流程有所瞭解.但是,許多細節的問題,例如像HttpRuntime如何建立HttpApplication物件等問題,仍然沒有解說清楚
當 然,瞭解這些細節並不是必需的,就算你不知道HttpApplication物件是如何被建立的,你仍能夠建立出好的元件.但是,如果你仔佃研究這些細 節,相信你會吸收不少技術養分.例如,通過本文講述的HttpApplication物件的建立過程,你就可以深切的體會到ASP.NET設計的精妙之 處,並且,你可以學到Factory設計模式的運用,這也是黃先生這本書的另一個特點,就是,他不光講述了ASP.NET的一些東西,同時教你一些程式設計的 方法和技巧,對於一些學習設計模式,而又覺得難以理解設計模式的初學者來說,通過閱讀黃先生的這本書,能夠讓你看到不少設計模式實地運用的講解.

實 際上HttpApplication並不是HttpRuntime所建立的.HttpRuntime只是向HttpApplicationFactory 提出請求,要求返回一個HttpApplication物件.HttpApplicationFactory在接收到請求後,會先檢查是否有已經存在並空 閒(可以這樣講嗎?比較形象一點)HttpApplication物件,如果有,則從池中取出一個HttpApplication物件返回給 HttpRuntime,如果沒有的話,則要建立一個HttpApplication物件給HttpRunTime(英文叫Pooling,好比你養魚一 樣,每個魚是一個HttpApplication,而你就是HttpApplicationFactory,別人問你要魚,你就會檢查池中有沒有現成的, 並且是合適的魚,有的話,撈一個給別人,沒有的話,創造一條魚,放入池中,再給別人,聽起來不錯,你好像威力無比喲)

實際上,我們再將 HttpApplication的建立過程放大來看的話,還有不少細節.上面講述的是HttpApplication是如何被請求以及如何被返回給 HttpRunTime,概括的來說,就是HttpRunTime不直接建立HttpApplication,而是把建立的權利交給 HttpApplicationFactory,這裡實際上運用的正是Factory模式,而且不是一般的Factory模式,而是帶有Pool能力的 Factory模式.HttpFactory物件負責建立並快取HttpApplication物件同時返回合適的物件給HttpRuntime.這裡就有一個問題了,也就是說,同一時刻,HttpApplicationFactory"養魚池"裡可能有很多個HttpApplication物件.那最 多他能夠同時快取多少個HttpApplication物件呢?預設情況下,HttpApplication物件的最大快取數目為100,並且 HttpApplicationFactory會迴圈釋放超過此數量的HttpApplication物件(那是不是意味著超出100個人同時訪問的系 統,無論設計的再好,也會因此而遭遇到效能瓶頸呢?)

HttpApplicationFactory
建立HttpRuntime的過程是 一個ParseCompile的過程.原書中黃先生說,HttpApplicationFactory會運用Parser物件來解析 Global.asax,同時,載入Global.dll檔案,同時,建立一個繼承自此類的原始碼,最後運用Compiler物件來編譯原始碼,再建立一 個HttpApplication物件.這一點,也可以從黃先生書中配圖可以看到.但是,估計許多人看到這裡已經暈了.你可以這樣理解這個過程:

首先,黃先生所言的ParserCompiler物件其實是指CodeparserCodeCompiler類的兩個例項,它們是.net類庫的一部分,Codeparser物件的作用是將一段文字轉換為一段C#VB原始碼
(
看 不懂嗎?有沒有想過,你在ASPX頁面中用中定義的ASP.NET控制元件在執行時究竟變成什麼東東呢?Parser物件可以負 責將以及類似的控制元件以及中的程式碼解析成對應的C#原始碼片斷,也就是 Label label2=new Label();的形式,它返回一個CodeCompileUnit物件.
如果你仍然不理解ASP.NET的這種Parser行為的話,你就不了 解ASP.NET控制元件的運作形式.你必須深刻的記住一點,那就是,處理對應的ASP.NET頁面請求的不是ASP.NET也不是IIS,更不是 ASP.NET頁面本身.而是ASP.NET運用PARSER物件將控制元件標記轉換成C#原始碼並且派生於PAGE,被編譯並例項外的一個物件.這一點非常 重要,ASP不同,我們知道ASP的程式碼是被載入到記憶體並且被ASP執行時解析然後返回HTML,但是,ASP.NET不是,ASP.NET處理頁面 請求的是一個類的例項,它是一個可以輸出HTML的物件.同樣的,HttpApplication物件的建立過程中,也是運用了Parser,因為 HttpApplication實際上要依賴於Global.ASAX檔案,而這個檔案,我們知道,如果不用CodeBehind來寫的話,它就是一 個,換句話來說,它也是一段標記,這段標記必須被轉換成C#原始碼,然後編譯成一個類,再產生這個類的例項,這個類就是HttpApplication.實際上,CodeParser,CodeCompiler以及Reflection等構成的CodeDom技術 是.NET核心部分之一,.NET的執行非常依賴於這些部分
)

至於parser為什麼要解析Global.ASAX同地又要載入Assembly,這一點上,許多人會想不通.因為如果使用CODEBehind技術的話,所有程式碼已經包含在Assembly中了呀,為什麼還要 解析Global.ASAX?如果你這樣想,你就錯了,首先,也可能Global.ASAX中定義了一部分函式,Assembly中定義了另一部分, 其次,沒有人說Gloabal.ASAX中只允許包含程式碼呀,也有人喜歡在其中利用OBJECT標記來定義APPLICATION範 圍或者Session範圍的物件呀,因此,parser有必要解析Global.ASAX檔案,將其轉換為C#程式碼片斷,然後,載入Assembly,利用Reflection技術建立一個繼承自類Global(預設情況下的類名)的新類的原始碼,然後將兩部分的原始碼合併(Ghost Application Class Source),運用Compiler建立出一個新類(Ghost Application Class Assembly),並且生成新類的一個例項來返回給HttpRuntime.

原 書中,黃先生說Parser會載入Global.dll檔案,我想,這可能是筆誤,很多人不明白Global.dll究竟是什麼.寫過ASP.NET的人 更知道,根本沒有這個檔案.我想,Global.dll就是包含global.asax檔案的工程編譯產生的Assembly,由於此Assembly包 含了CodeBehind方式下的Global類的資訊.因此,Parser才需要載入它

至此,HttpApplication物件建立完畢了.另外,AppDomain,HttpRuntime,HttpContext,HttpSession,HttpApplication,HttpModule這些物件的數量以及與使用者數量的對應關係做以下描述
每一個ASP.NET只有一個AppDomain,每一個AppDomain對應一個httpRunTime,它們都與使用者數量無關
每一個使用者對應一個HttpApplication,一個HttpSession,一個HttpContext和一組HttpModule

如果對於HttpApplication物件生成過程中的ParserCompiler不清楚的話(這部分很重要,包括AspX頁面的處理方法也是類似的),下面的圖或許有助理解:

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/25897606/viewspace-705193/,如需轉載,請註明出處,否則將追究法律責任。

相關文章