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