Asp.net MVC項目的部署(一):IIS以及Asp.net與IIS相關的部分
Asp.net MVC 1.0正式發布至今已將近四個月了,想必了解Asp.net MVC的人越來越多。打算寫一點關於如何部署Asp.net MVC的文字。
內容包括:IIS的版本變化,Asp.net的工作原理等。
涉及的東西比較基礎,內容也比較多,肯定會有我們已經知道的東西,但是為了完整性,可能會分(一)、(二)……如果你對Asp.net不太熟悉的話,可能閱讀中會遇到一些理解不了的地方,在這些地方我會給出推薦您閱讀的書籍或者園友的文章供你參考,如果你是Asp.net高級開發人員的話可以略過。
主要參考資料:Pro Asp.net MVC Framework.pdf(你可以搜索下載到)
部署,就是將你的網站發布到伺服器上以使人們可以實際使用。如果你曾經部署過Asp.net項目的話,你將會發現部署Asp.net MVC實際上跟部署Asp.net差不多,唯一特殊的地方是Asp.net MVC使用到了Routing,可能有人嘗試過在IIS6上使用無後綴URL的方法,如果你知道了下面這些列出的內容的話,就會變得很容易了。
lAsp.net MVC宿主伺服器所需的條件。
lIIS處理請求的架構,路由是如何與之融為一體的。
l安裝IIS6和7到Windows伺服器,將Asp.net MVC項目部署在他們上。
l配置你的Asp.net MVC項目。
該篇包含前兩個內容
伺服器環境
要運行Asp.net MVC項目,我們的伺服器需要滿足如下條件:
lIIS5.1或者更高版本的IIS,並且開啟了Asp.net服務。
l.NET Framework 3.5(最好同時安裝了SP1)
推薦的操作系統是Windows server 2003(IIS6)和Windows server 2008(IIS7)。Asp.net MVC本身不要求伺服器必須安裝了它。因為我們將System.Web.Mvc.dll和你或許用到的Microsoft.Web.Mvc.dll直接放在Bin文件夾中部署就可以了,這種部署方式叫做私有部署,如果你買的空間沒有安裝Asp.net MVC的話(即GAC中沒有上述的兩個dll)通過私有部署的方式也很容易,另外如果你的伺服器沒有安裝過.NET Framework SP1的話,你還需要將System.Web.Abstractions.dll和System.Web.Routing.dll也拷貝到你的Bin文件夾中。下面會詳述這些。
Asp.net MVC對我們買的虛擬伺服器有什麼樣的要求
除了需要伺服器支持Asp.net 2.0和安裝了.NET Frameworkd 3.5之外沒有其他要求。所以我們看不到有空間提供商打廣告說自己的空間支持Asp.net MVC,因為通過把相關的程序集放在Bin文件夾中你同樣可以私有部署你的Asp.net MVC項目。
如果你的伺服器提供商用的是IIS7,那麼IIS7與Asp.net的集成管線模式可以給你帶來乾淨的無後綴的URL,如果是IIS6的話雖然也能夠做到,但是就沒有IIS7那麼高效、方便和容易了,下文會有關於Windows server 2003/IIS6的內容。
關於IIS
lIIS6/ Windows Server 2003。
lIIS7/Windows Server 2008。
lIIS 7.5/Windows Server 2008 R2(尚未發布)。
我們僅僅介紹6.0之後版本的IIS,下面我們來快速覆蓋一下IIS的知識,包括:虛擬路徑、綁定設置和應用程序池。最後談一下IIS內部是如何處理請求的(IIS7是如何處理無後綴的URL的)。
理解網站和虛擬目錄
IIS可以同時宿主多個不同的網站,我們對每一個網站指定一個根目錄,這個目錄可以是伺服器的本機目錄也可以在網路的其他地方,然後IIS就可以從它所管理的那些目錄下尋找或獲得相應靜態或動態請求的內容給我們了。
為了指導特定的HTTP請求到相應的相應網站,IIS允許我們配置「綁定」。所謂「綁定」就是將一個特定的IP、TCP埠號、和HTTP主機名對應到特定的網站。如下圖:
說明:Windows7/IIS7.5 作為一個額外的配置,你還可以在網站目錄文件夾任意層級上添加虛擬目錄,每一個虛擬目錄表示IIS將從其他地方提取或獲得內容返回給對該虛擬目錄的請求,這取決於你建立的虛擬目錄所指定的文件夾的位置(它同樣可以在本機也可以在網路的其他地方)。虛擬目錄的目的是讓真實文件所在的位置與網站目錄列表脫離關係,有點類似文件夾的「快捷方式」,虛擬目錄的存在使得外界不知道我們的真實文件所在具體位置,個人感覺邏輯上的意義大於安全上的意義。 說明:IIS7/IIS7.5中虛擬目錄的顯示 對於每一個虛擬目錄你還可以選擇是否賦予它獨立的應用程序地位。如果選擇這樣做,該虛擬目錄對應的獨立的應用程序就擁有自己獨立的配置文件了,如果該獨立的應用程序是個Asp.net應用程序的話,那麼它的狀態也是獨立的啦,是與它的父級應用程序的狀態無關的。顯然,因為他們是相互獨立的應用程序,所以被設置成獨立的應用程序的這個虛擬目錄中所運行的Asp.net完全可以是和父級不同的版本的Asp.net。 從IIS6開始,IIS引入了應用程序池(application pools)。應用程序池用來隔離同一台伺服器上的多個同時運行的應用程序,每一個應用程序池工作在一個獨立的工作進程中,設定相應的最大內存和CPU使用量,進程回收時間表,等。每一個網站或設置為獨立應用程序的虛擬目錄應用程序都會被分配到建立起來的IIS的應用程序池的其中一個池中去。一般的話,每個應用程序應建立一個應用程序池而不是與其他應用程序共用。這樣可以保證如果一個應用程序崩潰了不影響其他應用程序的正常運行。參考:應用程序池 綁定網站到主機名、IP地址和埠 因為同一個伺服器可以宿主多個網站,所以就需要一個系統來分派請求到正確的Web應用程序。上文提到,我們可以綁定網站到一個或者多個:
l埠號
l主機名
lIP地址(僅當伺服器有多個IP地址時——比如有多個網路適配器)
對於主機名和IP地址,你可以選擇不做任何指定,不做任何指定等於是一個通配符,這樣的話對於所有不匹配特定網站的請求就會匹配給它。如果多個網站具有相同的綁定設置的話,同一時間只可能有一個是可用的,其它的處於停掉的狀態,否則就不唯一了,對吧。虛擬目錄繼承父級應用程序的綁定設置。
IIS是如何處理進來的請求並調用ASP.NET的
當IIS將接收到的一個請求分配給相應的網站的時候,它需要決定怎麼來處理這個請求。它是要直接從磁碟返回一個靜態內容呢,還是要調用網站應用程序執行它並動態地生成內容來返回呢?它是如何決定這些的?
作為一個Asp.net MVC程序域,你需要理解IIS的這個機制,不僅是Asp.net MVC程序員,Asp.net程序員都需要理解這個機制(Asp.net程序員包含Asp.net MVC程序員)——至少應該有個基本了解;否則,你將會在理解進來的請求與你的路由配置映射時遇到困難。
IIS6和IIS7在傳統模式下是如何處理請求的
如果你沒有使用IIS7的集成管線模式的話,你使用的將是回歸到IIS5的傳統管道模型。在這個模式下,IIS只能提供靜態內容和具有特定擴展名的從而可以映射到相應的ISAPI的URL返回的動態內容。
IIS分析請求近來的URL,取得它的擴展名(比如:http://hostname/folder/file.aspx?foo=bar的擴展名是.aspx),將該擴展名發往相應的ISAPI擴展程序。對於IIS6和IIS7來說,你都可以配置ISAPI擴展程序與擴展名的映射,對於IIS7來說,你還可以使用處理器映射配置工具(裝的不是中文版IIS翻譯不準,英文為Handler Mappings configuration tool),如下圖所示。
說明:Windows7/IIS7.5 上圖中*.aspx被配置給了aspnet_isapi.dll,aspnent_isapi.dll這個非託管的dll在操作系統啟動時就已經被IIS載入到了內存中,該dll與託管環境進行交互然後將控制權轉交給該應用程序所在的隔離的應用程序域的.NET CLR,CLR接到控制權後,接著實例化一個HttpRuntime類對象,然後調用該HttpRuntime實例對象的ProcessRequest方法從而驅動後續的處理,當ProcessRequest執行結束的時候封裝請求上下文信息的HttpContext實例就被構建完成了,繼續,進入Asp.net運行時管道(可能叫做管線會更好些,MSDN上翻譯的是管線),這個管道就像是條工廠中車間的流水線,而這條流水線上加工的對象就是前面HttpRuntime實例化出來的那個封裝請求上下文信息的HttpContext實例對象,每個HTTP模塊和管道後端的HTTP處理程序都是一道加工工序,這些工序有的負責驗證許可權,有的負責Asp.net的狀態管理等,我把這個過程不確定的歸結為職責鏈模式(C:WindowsMicrosoft.NETFrameworkv2.0.50727CONFIG路徑下<httpModules>節點中註冊了這些默認的HTTP模塊),接下來就是在管道中按照HTTP模塊註冊的順序挨個執行各個HTTP模塊,最後到達管道尾端的HTTP處理程序,比如我們的Page類就是一個實現了IHttpHandler介面的處理程序。再往下就是控制項以及頁面生命周期等這些我們非常熟悉的東西了。值得說明的是,在我們的Global.asax中從System.Web.HttpApplication類繼承而來的那個自定義類中按照約定的方式書寫代碼就是我們與所有這些Http模塊們交互的方式了。其實對於System.Web.UI.Page的實例化實際上是通過工廠來返回的,上圖註冊*.aspx的時候就是註冊到了默認的PageHandlerFactory工廠了,該工廠實例再根據前面所有那些步驟中一直存在的最後封裝到了HttpContext中的請求的URL對應的文件名來查找相應文件名的的真實的.aspx文件,並讀取該文件的第一行指令,指令指定了最終負責處理該請求的HTTP處理程序類,這個類就是我們編寫的System.Web.UI.Page類(一般是從Page繼承過來的子類)。接下來伺服器控制項與組件以及頁面的執行最終生成HTML頁面內容,生成的HTML結果返回給開始的aspnet_isapi擴展最後被IIS輸出。而控制項以及頁面生命周期等這些已經是我們非常熟悉的領域了。有興趣的話你可以翻閱MSDN或者覺得有什麼意義的話更進一步用Reflector沿著流程從System.Web.UI.PageHandlerFactory開始查看源代碼。 說明:Http模塊就是實現了IHttpModule介面的類,Http處理程序就是實現了IHttpHandler介面的類。老趙說過:如果你不理解Http模塊和Http處理程序的話就不能稱為真正熟悉Asp.net,之所以要理解HttpModule和HttpHandler是因為理解了他們之後才能明明白白地基於HttpModule和HttpHandler進行編程,擴展我們的Asp.net應用程序管線中的內容(原話可能不是這樣,意思應該是這樣)。如果你想了解HttpModule和HttpHandler的話,有兩本書非常值得推薦:微軟出版社的《Asp.net 3.5技術內幕》和Wrox的《Asp.net 2.0伺服器控制項與組件高級編程》這兩本書的相關章節都有對該主題較好的介紹。閱讀小洋(燕洋天工作室)的淺談ASP.NET內部機制系列或者張子陽的關於Asp.net的文章也是一個不錯的選擇。Asp.net MVC在HttpModule和HttpHandler級別上與原來的Asp.net是完全一樣的。 Asp.net是如何被關聯的 在你安裝.NET Framework的時候(或者執行aspnet_regiis.exe的時候),安裝程序自動註冊了*.aspx,*.axd,*.ashx,和其他一些擴展名到一個特殊的名字叫做aspnet_isapi.dll的ISAPI擴展程序。一個請求必須與一個註冊過的擴展名相匹配,然後IIS將激活aspnet_isapi.dll,這個非託管的dll再把控制權轉交給託管代碼,接下來就是託管代碼的時間了,.NET CLR在一個不同的進程中執行這些接下來的非託管代碼。 無後綴URL的問題 傳統上,該系統對Asp.net伺服器頁面來說一切正常,因為它們對應真實的以.aspx為後綴文件真實的存在與磁碟上。但是,對於新的路由系統來說就不是這樣了,對於routing來說URL不需要與磁碟上的真實文件對應甚至不需要有擴展名。 這個新的URL路由系統是作為一個.NET HTTP模塊創建的。該HTTP模塊是被假設為處理所有請求來創建的,該模塊是判斷和決定對一個請求的控制是否可以轉入到你的Asp.net MVC項目的某個控制器並由該控制器來接待。但是這是.NET託管代碼,所以只有在請求能夠激活ASP.NET的條件下才能夠往下進行(比如IIS將請求映射給了aspnet_isapi.dll)。所以除非該請求URL具有合適的擴展名,否則aspnet_isapi.dll根本不會被激活,這意味著IIS將會把該請求作為靜態請求嘗試返回相應於URL的靜態文件的內容,因為磁碟上並沒有這個靜態文件存在,所以我們將得到一個404 Not Found錯誤。如何才能做到可以使用無擴展名的URL呢!我們大部分希望將Asp.net MVC項目部署到IIS6上的人開始都會遇到這個問題。接下來的文字中我們會給出四個解決方案以供選擇。 這部分內容太基礎和透明,很難把握,對於存在的不正確的地方希望朋友們指出來,回頭改正以免誤導。下一篇真正進入Asp.net MVC的部署。————————————————添加於2009.9.19
ASP.NET執行循序
首先第一次運行一個應用程序(WebSite或者WebApplication都是Web應用程序)第一次請求 ->
1,IIS ->
2,aspnet_isapi(非託管dll) ->
3,HttpRuntime(到這裡已經是託管的了)HttpRuntime中只有一個方法ProcessRequest 這個方法是整個應用程序的入口點 HttpContext就是在這個方法裡面構建的 出了這個方法後HttpContext就構建完成了 ->
4,執行HttpApplication類的Start方法(因為是第一次請求,HttpApplication還沒有建立即應用程序池中還沒有該站點的HttpApplication對象存在,再第二次請求的時候應用程序池中就已經有HttpApplicationd對象了就不會執行這個方法了 只有應用程序池裡沒有該站點的HttpApplication對象的時候才會執行這個方法) ->
5,初始化各個HttpModule在HttpModule的Init方法中註冊HttpApplication的事件方法 而Init中的HttpApplication就是那個新建立的或者從HttpApplication池中得到的那個->
6,按照順序執行HttpModule們註冊給HttpApplication的事件方法 ->
7,首先是Application的BeginRequest事件方法 這個事件的方法列表中的方法分散在任何地方 在HttpModule里有該事件的方法 所以後續的執行回反覆進入HttpModule中去執行這些註冊給HttpApplication事件的方法 其中HttpApplicaiton的事件方法在HttpApplication自己裡面是按照約定的方法註冊的 這裡是約定的編程方式必須加上"HttpApplicaiton_"前綴 在HttpApplication_Start後的某個時候使用反射註冊這些約定命名的方法到對應的HttpApplication的事件列表方法 ->
8,接著按照HttpApplication中事件的順序執行註冊給HttpApplication餘下事件的事件方法 在HttpApplication執行到中間的某個環節的時候開啟執行HttpHandler HttpHandler執行完了 Application的最後的事件是EndRequest 執行HttpApplication的EndRequest事件方法列表中的方法 這些方法有的在HttpModule中所以最後又進入HttpModule HttpModule的Init方法是給HttpApplication註冊事件方法的唯一地方 最後看註冊給HttpApplication的事件方法列表總方法都是分別在哪裡 由HttpApplication的事件執行順序決定整個管道中的流程 HttpModule的Init方法是唯一一個可以訪問HttpApplication對象的地方 HttpModule在Init里給這個HttpApplication對象註冊事件把HttpModule的自己的方法作為HttpApplication的事件方法橫插在HttpApplication的事件流程中
HttpApplication的各個事件執行順序就是所謂的管道 當一個請求進來的時候 IIS根據請求的Url把請求交給相應的站點 如果該站點是ASP.NET支持的話 HttpRuntime從HttpApplication應用程序池中取出一個HttpApplication對象 然後把這個取出來的HttpApplication對象交給HttpModule的Init(HttpApplication application)方法 HttpModule再給這個HttpApplication的「事件」插入「事件方法」 在HttpApplication中間會通過HttpHandlerFactory根據請求的Url的文件名以及擴展名決定經過哪一個HttpHandler 因為HttpHandler不是每一個都要經過的而是取決於Url的文件名和擴展名所以HttpHandler必不能保有對HttpApplication以及HttpModule這些對於每個請求都會經過的對象 否則的話HttpHandler就可以編程HttpApplication了 而這個HttpApplication會被放回HttpApplication應用程序池 就是網站應用程序池 也就是IIS上建立的那個池 那個池中存的就是該站點的HttpApplication對象注意:HttpApplication被放入IIS的ASP.NET Web應用程序對象池的事件是在所有的HttpModule的Init方法被執行之後 第二次請求的時候是不會再經過HttpModule了 但是因為有的HttpModule在Init中把自己的方法註冊給了HttpApplication的事件 所以後面才會反覆進入HttpModule去執行HttpApplication的事件方法 如果HttpModule的Init方法不是把自己的方法註冊給HttpApplication而是這個方法在其他地方那就不會再進入HttpModule了 也就是HttpModule里只有一個方法Init這個方法跟HttpApplication的Start方法一樣是只會被執行一次的
推薦閱讀:
※男人女人的生活目的 (原創)
※以結婚為目的的談戀愛都是耍流氓
※在看詩歌題目的時候,我們都能看到些什麼?丨詩歌鑒賞
※賞心悅目的春景【美景欣賞】
※【小番整理】多個熱門目的地出入境卡中文模板匯總