ASP.NET中ViewState概念
記得數年前,當ASP.NET剛出現時,天下間Web開發(fā)框架中似乎出現了一個“巨人”,WebForms這種似乎人人都能掌握的開發(fā)框架幾乎瞬間流行起來。如果誰還在用傳統(tǒng)ASP這種控制與表現混合的開發(fā)方式,似乎立即變得低俗了許多。于是乎許許多多人都學會了拖控件+綁定的方式,“Web開發(fā)人員”也越來越多,一片紅火,好不熱鬧。
風水輪流轉,不知從什么時候開始Rails框架隨著RoR忽的流行了開來,.NET社區(qū)也出現了Monorail,批判WebForms聲音也慢慢多了起來。如今微軟自己也推出了基于ASP.NET平臺的MVC框架,很多WebForms的反對者似乎更加自信了:連微軟自己都拋棄了WebForms,證明WebForms的確該退出歷史舞臺了,也聽到了一些類似于“WebForms不適合Web開發(fā)已經是公認的事實”這樣“無比肯定”的話。先不說微軟推出MVC到底是不是意味著它拋棄了WebForms,單從那些MVC追捧者們“念念不忘”的WebForms的缺點上來看,我認為他們大部分只是在“跟風”,就和當年許許多多人追捧WebForms一樣。
不過我必須承認,我對ASP.NET MVC的了解僅限于Scott Gu博客上所寫的內容,至今還沒有下載過ASP.NET 3.5 Extensions CTP。而對于RoR和Monorail也僅限于一些資料和示例,從來沒有寫過一行代碼。按照我的“標準”,我自己是沒有資格評論MVC框架的優(yōu)劣的。不過我還是想寫這篇文章,因為我只會WebForms平反,而不會“貶低”MVC框架;我只是想證明WebForms的那些缺點到底真的是缺點,還是開發(fā)人員自身沒有好好利用起這把利器。因此我將會根據我的經驗,一一回應對WebForms比較常見的指責。如果措辭上有任何的不妥,也請大家多多包涵。
我下面提到的做法,都是在經過實際開發(fā)過程檢驗的(例如開發(fā)人員與美工的合作),可能不是最佳,但是我認為還是不錯的。
一、ViewState概念
HTTP是無連接無狀態(tài)的協(xié)議,因此ASP.NET中提出了ViewState概念,這樣數據被重新Post回頁面時,頁面(控件)的狀態(tài)就能恢復,因此才有了很多豐富的功能,例如一些復雜的控件事件。但是ViewState帶來的問題就是,如果使用不當,那么頁面體積就會增加許多,網絡中傳輸的數據太多自然會影響性能。
但是 ViewState真是必須的嗎?我可以很負責任地說,在如今大部分Web應用的頁面中,出現的幾乎都是大量的鏈接,點擊鏈接就會跳轉到一個和當前頁面完全無關的新頁面,這樣的話,頁面上的ViewState又有什么用?因此我如果新建一個Web項目,做的第一件事情就是去Web.config中將 enableViewState從全局關閉——同時關閉的還有enableSessionState,這也是影響性能的因素之一(stateless也便于做Web服務器層面的負載均衡)。
有人曾經反駁我,關閉了ViewState,用WebForm還有什么意義?我的答案是:意義多的很。WebForm提供了控件模型,我能夠使用“人人都能看懂和編寫”的方式來設置或讀取一個文本框里的值。我能輕松地響應不同按鈕的事件來編寫觸發(fā)各種業(yè)務邏輯。這就是意義,WebForms的開發(fā)還是非常簡單而清晰的(在一定程度上吧,不要“濫用”永遠是正確的)。
嗯?剛才不是說只有保持ViewState才能使用控件的事件嗎?沒有ViewState怎么從控件中重新獲取狀態(tài)呢?請注意我之前所說的是“復雜事件 ”。什么是復雜事件?TextBox的TextChange事件就是“復雜事件”,GridView的Command事件也是復雜事件,但是Button 的Click事件就是“簡單事件”;與此相對的,GridView里的每一行的數據每一個子控件的狀態(tài)是“復雜狀態(tài)”,而TextBox的Text屬性則是“簡單狀態(tài)”。“復雜狀態(tài)”和“復雜事件”需要ViewState,因為與之有關的這些“控件”是ASP.NET“無中生有”的,但是“簡單事件”和“簡單狀態(tài)”基于頁面中“必然”會提交的數據,它們自然能夠還能夠使用。在我的ASP.NET開發(fā)過程中,使用的幾乎都是“簡單事件”和“簡單狀態(tài)”,而印象中放棄“復雜事件”和“復雜狀態(tài)”并沒有給我?guī)砣魏蔚睦_。
當某人送給我們10件禮物,而其中只有4件是我需要的,那么為什么不能簡單地放棄其余6件,偏偏要去感謝只送給我們3件禮物的人而去指責前者呢?要知道他并沒有惡意,那多余的6件也沒有給我們造成任何困擾。
但人就是那么奇怪。
二、性能
WebForms的一個重要特點就是一個強大(很多情況下也是“復雜”的代名詞)的組件模型。這個組件模型包含一個叫做“生命周期”的玩意兒,也就是這個玩意兒被不少人指責為性能殺手。這個復雜的生命周期的確在很多時候只是“無謂”地一遍遍執(zhí)行,似乎的確造成了“浪費”,但是這真的到了“殺手”級別了嗎?
如果您認為這個組件模型為性能殺手,不如編寫一個內置1000個動態(tài)Button控件的頁面,然后部署到服務器上,我保證運行的飛快。1000個不夠的話那么可以試試看3000、5000甚至10000個控件。您哪張頁面上控件的數量會比這個還多?但是您多少頁面的性能會比它高?也有文章說“盡可能少的使用服務器端控件,最多使用HTML控件加上runat=server”,這更加沒有理由了:一個加了runat=server的HTML控件,它已經變成了服務器端控件了。而普通的HTML最后在控件樹中僅僅被作為普通的文本而處理,在控件樹中是用一個Literal保存其中的“字符”。至于具體內容是什么,ASP.NET根本不會關心。
造成性能問題的原因多種多樣,在對性能問題進行探索和優(yōu)化之前,一定要找準性能瓶頸是什么,才能對癥下藥。如果從某些層面上講,將公共部分提取成新的方法,會造成執(zhí)行上多一次call指令的執(zhí)行,性能也就“降低”了,但是我相信沒有人會因此將同樣的代碼到處復制。在我們接觸到的Web應用中,性能瓶頸大都是在數據庫訪問上(或者外部Service訪問,等等),多執(zhí)行一次數據庫查詢操作可能就能抵得上內存中1億次引用拷貝。我相信,如果一個ASP.NET應用程序的性能不高,幾乎不可能是因為組件模型或生命周期造成的問題。
既然Web應用瓶頸大都在數據庫訪問上,那么一般該如何解決這個問題呢?最直接的方式應該是優(yōu)化數據庫的查詢,但是最關鍵的可能還是緩存。君不見每個談到Web應用性能優(yōu)化的講座都將Cache放在數一數二的位置上,因為這的確是最有效的優(yōu)化方式之一。在一個并發(fā)較高的Web應用中,對一些數據進行1分鐘的緩存也能帶來相當可觀的性能提高。其他的方式可能還有生成靜態(tài)頁面(沒有比這訪問速度更快的了),異步調用(例如一篇剛發(fā)布的文章,在數分鐘后才能被搜索到也沒有關系,那么何必一定要同步地、即時地寫入數據或者創(chuàng)建索引呢?)、分離不同作用的服務器(可以為不同服務器進行有針對性的配置,例如分離圖片服務器),做Web服務器端的負載均衡(stateless的重要性由此可見),對數據庫進行縱向切割(加快內存中載入的數據量可以提高查詢性能,并且縱向切割后能夠使用多臺數據庫服務器分擔壓力),橫向切割(sharding,將數據分置在不同的數據庫中,以此可以通過scale out來擴展減少每臺服務器的負載,提高性能),作數據冗余或Master-Slave(稍稍降低寫操作的性能而提高讀取數據的性能,普通Web應用大都 “讀取”遠多于“寫入”)等等。
當然我上面提到的都是應用程序實現和架構方面的東西,事實上開發(fā)一個高性能Web應用還涉及到硬件/ 軟件/操作系統(tǒng)等多方面,這里就不多解釋了(其實這方面我也還在探索過程中)。其實我在這里想說的仍然是,開發(fā)高性能Web應用程序的關鍵大都與具體所用的實現技術無關:只要“實現”正確,做法大都相同,無論Sql Server/Oracle,Windows/Linux還是ASP.NET/RoR,其本質都差不多。Ruby和C#的性能相差十倍(存疑,求證),不還是能夠開發(fā)出高性能的Web應用嗎?以上介紹ASP.NET中ViewState概念。
【編輯推薦】