前一階段由于頁面上的性能出了問題,于是進(jìn)行了一次性能上的優(yōu)化,時間也過了一陣了,想總結(jié)出一些東西來,可是一直沒有時間,今天下班早一些,趕緊整理一下思路,以免以后忘記了。
我們的框架配置:Struts2+Spring+Ext2.1
頁面布局:主頁面=menu+toolbar+tabPanel ,每一個模塊一個TAB頁的形式展現(xiàn)出來。(很傳統(tǒng))
我們經(jīng)歷的三次重構(gòu):
1. 從普通的JSP+HTML+自定義TAG --》 EXT
2. 多個JSP的TAB頁面嵌套--》One Page One Application
3. JS全部一次性加載--》JS依賴關(guān)系懶加載+JS壓縮
這三次重構(gòu)的方案實際上是我們在EXT開發(fā)過程中的不斷的犯錯誤中的教訓(xùn)總結(jié)。
下面我就從這三次重構(gòu)中總結(jié)一下每一次的出發(fā)點及動機(jī),經(jīng)歷的磨難,最后的成效。
第一次,從JSP+HTML+TAG --》 EXT2.1
這一次的重構(gòu)在我們項目來看是一次歷史性的變革。項目在進(jìn)行過程中,前期大量的需求調(diào)研,并與客戶進(jìn)行拍板定需求,可是客戶都是打太極的高手,他們不會告訴你他要什么,他也不會拍些板來說這個就這么做了,所以我們對于需求上的定義始終是模糊的。他們對系統(tǒng)的要求也是相當(dāng)?shù)母?,會要求在BS上的架構(gòu)上進(jìn)行數(shù)據(jù)實體的連線,會要求多種多樣的表現(xiàn)形式。
我們先從靜態(tài)頁面進(jìn)行了初期的規(guī)劃,畫了一版靜態(tài)頁面,然后客戶對些不發(fā)表任何意見,要求一個能連貫起來的系統(tǒng)。于是我們以最快的速度在短時間內(nèi)使用JSP+一些自定義的TAG+HTML+CSS畫出了一些有較多功能性的模塊,在這過程中,客戶對頁面不斷的提出要求,要更多的用戶體驗,對我們所畫出來的列表,表格,樹以及布局都提出了較高的要求,并一再的埋怨我們的頁面過于死板。在客戶的不斷壓力下,領(lǐng)導(dǎo)提出了一個相當(dāng)有風(fēng)險的提議。使用當(dāng)前國內(nèi)界內(nèi)用戶量并不是很大,使用也不是很成熟的EXT。
我在以前的項目使用過一段時間,只有少量的經(jīng)驗,這個決定很超出我的想像。在這一點上,我有很多東西需要向領(lǐng)導(dǎo)們學(xué)習(xí)。領(lǐng)導(dǎo)給我們的預(yù)研時間是一個星期。由于成手很缺少,于是項目內(nèi)組織幾個以前有過頁面開發(fā)經(jīng)驗的一年的員工進(jìn)行技術(shù)預(yù)研。這一周時間里,我們的預(yù)研人員數(shù)量還是不錯,將EXT的版本定義在2.1這個相對較新的版本,將各個類型的控件分配到下面的每一個人手里,一周的時間里,大家對各自的控件都有了一定的了解。
其實這里差一點忘了一個更大的風(fēng)險,我們的框架也是在這個時間里誕生的,之前公司內(nèi)推的技術(shù)框架被領(lǐng)導(dǎo)貶的一文不值,于是我們有了搭建新的框架的想法。我們在這兩周里,將框架的層次,各個層次的交互,異常的處理,事務(wù)的控制以及數(shù)據(jù)庫的操作都定義出來了。其他的設(shè)計機(jī)制我們是在接下來的時間里完成的,但是前面所說的這幾樣最基本的模型是在這段時間里產(chǎn)生的。
經(jīng)歷了這兩周的時間的預(yù)研過程,我們在新的框架上建立了一個相對較完整的DEMO,將各個層次之間也一起串聯(lián)起來了。
時間很短,這里面領(lǐng)導(dǎo)的開發(fā)經(jīng)驗和天賦很讓我佩服,我主要的工作是在前后臺的交互,異常的處理和DEMO的開發(fā)上,并主要負(fù)責(zé)指導(dǎo)其他開發(fā)人員使用EXT的預(yù)研。
總結(jié)一下這一階段的主要開發(fā)思路,其實這一階段主要將EXT的使用進(jìn)行普及,使所有的開發(fā)人員可以基本熟練的使用EXT進(jìn)行業(yè)務(wù)模型的實現(xiàn),并解決處理業(yè)務(wù)中的一些問題。所以這一次重構(gòu)基本談不上優(yōu)化,只是一次功能與業(yè)務(wù)在新框架上的遷移。
第二次,將多頁面的EXT模塊開發(fā)轉(zhuǎn)為one page one application
我們新搭建的框架在根本上還是以JSP和JAVA做為開發(fā)語言,雖然前臺使用了EXT,大量的使用了AJAX的調(diào)用,但是JSP在服務(wù)端的編譯還是避免不了。
同時,由于我們系統(tǒng)的特殊性,我們的業(yè)務(wù)的復(fù)雜性,我們在整個系統(tǒng)中,嵌套了多個頁面,擁有多個JSP的嵌套。我們在第一階段的時候,所有的模塊的嵌入方式就是以iframe來進(jìn)行嵌套。因此在這里,由于系統(tǒng)的復(fù)雜性,在最復(fù)雜的一個頁面上,竟然嵌套了六層iframe。加上JSP的編譯時間,再加上每一個頁面上都引入了ext-all.js 和ext-base.js ,以及一些公共的JS文件,怎么可能不慢。我們粗略的計算了一下。最慢的頁面打開要超過兩分鐘,客戶端的硬件配置如果糟一點就更慢了。
在這種環(huán)境下,我們不得不進(jìn)行第二次的頁面重構(gòu),進(jìn)行性能的調(diào)優(yōu)。
分析一下這一次的重構(gòu),其實我們主要的問題在于業(yè)務(wù)的復(fù)雜性,數(shù)據(jù)量大,以及開發(fā)人員開發(fā)過程中的經(jīng)驗不足。
從框架的設(shè)計上來說也是有著嚴(yán)重的不足,其中大量的使用iframe就是一個問題,經(jīng)過一些專業(yè)的測試軟件測試,發(fā)現(xiàn)很多的iframe關(guān)閉后 ,該頁面所持有的DOM節(jié)點并未釋放,后來做了特殊的處理,也同樣是無法全完釋放頁面上的內(nèi)存占用。每一個頁面上都引了ext-all.js,就單單這一個JS的基礎(chǔ)類庫文件,就足足有近500K,雖然多個頁面引用后下載只下載一次,從緩存文件中讀取,但是每一個頁面要想正確的加裁EXT的控件,是需要對這個基礎(chǔ)類庫進(jìn)行編譯的。這樣的話,多個頁面進(jìn)行嵌套的時候,就面臨著這巨大的性能問題。
結(jié)合上述的問題所在,我們進(jìn)行第二次的重構(gòu)。主要的方案就是將過多的iframe進(jìn)行合并處理,大量的iframe進(jìn)行整合,減少ext-all.js的加載次數(shù),并精減掉iframe。這樣就從很大程度上減少了js文件的編譯時間,重復(fù)JS編譯時間。
最后我們精減后的頁面框架是主頁面上有一個JSP,上面加載了所有的JS,以前用iframe來框起來的頁面都用Ext的panel來代替,從頁面的編譯時間上來看,是大大的減少了這方面的消耗。同時在副頁面上,原來的多層的JSP也都使用panel來代替,其中將activeX控件也放在了panel中。
經(jīng)過了重構(gòu)后的系統(tǒng),在JS下載及編譯的時間上大大的減少了,jSP的引用數(shù)量少了,編譯時間也縮短了。于是在性能上有了一個飛越。
可是好景不長,我們在重構(gòu)基本完成的時候,性能問題再度暴露。
第三次,從首次完全加載,到動態(tài)加載JS。
由于使用了one page one application的思想,我們的頁面減少了,可是JS并沒有減少,一個頁面上引用了大量的JS,其中算上基礎(chǔ)的ext-all.js等文件,我們在一個頁面上加載了170+個JS文件,其數(shù)量真是可觀,其性能也足以想像得到了。
由于這個企業(yè)應(yīng)用的復(fù)雜性,我們的JS想減少是不太可能了,我們現(xiàn)在就是按功能點進(jìn)行劃分JS對象的。其中很多JS也都進(jìn)行了合并。但是數(shù)量依然不減。
分析一下這次重構(gòu)的原因,JS的數(shù)量過多,第一次加載頁面的時候,會非常慢,但是加載過后,操作會較快,這個其實也是當(dāng)初預(yù)料到的一個風(fēng)險,但是沒有想到會這么嚴(yán)重。有些低估了。同時,由于都在一個頁面上,這個頁面加載的DOM節(jié)點超級多,用監(jiān)控軟件可以看到節(jié)點的遞增,并有部分節(jié)點不能正常釋放,這個也是EXT官方論壇上公認(rèn)的一個問題。要想從根本上去把這個問題解決了我們沒有這個精力,也沒有這個能力和時間。
后來,我們?yōu)榇颂匾饨M織了一個性能優(yōu)化小組,單憑我一個人是解決不了了,畢竟人多力量大。經(jīng)過幾次會議和幾種方案的驗證,我們做了如下的計劃和方案。
1. 將JS進(jìn)行合并壓縮。
使用yahoo的yui-compress.jar進(jìn)行壓縮JS,去掉過多的空格和注釋,并合并,減少IO的支出。
2. 將前后臺傳輸?shù)臄?shù)據(jù)進(jìn)行GZIP壓縮。
大數(shù)據(jù)量的數(shù)據(jù)傳輸,通過GZIP的壓縮方案,可以減少到25%,有些數(shù)據(jù)可能會更多。
3. 對大量的JS分析依賴關(guān)系,進(jìn)行動態(tài)加載。
這個是關(guān)鍵,通過分析所有的JS中的依賴關(guān)系,減少了JS加載的數(shù)量。從很大程度上提高了性能。
4. 另外對部分頁面進(jìn)行緩存,而非真正的關(guān)閉。
對于Ext的消毀不能完全釋放節(jié)點,那我們就不消毀他,進(jìn)行對象的緩存,并在重新打開該功能的時候進(jìn)行數(shù)據(jù)的重新加載和重置功能,對象重用。
經(jīng)過上述的四種方案后,我們的頁面性能有了大幅度的提升,由原來的35+秒,提升到首頁面加載只需要3秒之內(nèi)。
其中將依賴關(guān)系進(jìn)行分析后,我們定義了依賴關(guān)系映射文件,將首頁面上首先要加載的JS進(jìn)行壓縮合并,分塊顯示,同時將不需要一進(jìn)入頁面就要顯示的模塊進(jìn)行懶加載,在使用的時候再從服務(wù)端DOWN下來進(jìn)行編譯,同時使用了數(shù)據(jù)的GZIP壓縮,頁面也進(jìn)行了緩存。
還有一個外部的因素,由于系統(tǒng)使用的客戶機(jī)環(huán)境上的復(fù)雜,我們在多個瀏覽器上進(jìn)行了測試,只有IE是最慢的,尤其是IE6,后來發(fā)現(xiàn)不是IE6要比IE7慢,是因為發(fā)現(xiàn)MS發(fā)布了腳本引擎cscript 5.7, 而大部分的ie6系統(tǒng)都裝的是5.6, 這個版本上的升級,不僅僅是修改了BUG,在JS的執(zhí)行速度上也有了較大的提升,于是我們在環(huán)境因素上又加上了一條,要求客戶安裝cscript5.7,也大大的提升了頁面的打開時間。
經(jīng)過了上述的三次頁面重構(gòu),我們的系統(tǒng)基本上在頁面的性能上趨于穩(wěn)定,基本上滿足了客戶對于性能及用戶體驗度上的要求。為此特定總結(jié)如下,感興趣的朋友可以參見一下,有不明白的地方我們多交流,隨著時間的推移,我會將這次項目中的很多經(jīng)驗都寫出來,與大家分享。