解決 ASP.NET 網站重新編譯導致反應龜速的問題
1. 問題
我們碰到的問題是,ASP.NET 網站的程式檔案數量很龐大,造成每次有共用的類別檔案(例如 App_Code 底下的檔案)更新時,「享用」此更新版本的第一個使用者就會因為整個網站重新編譯而等個好幾分鐘,網頁才出得來(其實看起來就像網站當掉了)。
網站的程式檔案數量有多大呢?我把圖抓下來:
22,714 個檔案,扣掉一些圖片、CSS、靜態網頁等程式檔案,保守估計也有兩萬個程式檔案。由於這個專案還挺大的(對某些人來說,這樣的規模或許還算普通吧),裡面很多子系統都還在陸續開發中,因此經常會有 user 火線作業時必須立即更新程式檔案的情況,例如:緊急 bug 修正。
結果,每當更新程式時,整個 ASP.NET 網站就像掛掉一樣,完全無法回應。用工作管理員觀察,可以看出是 ASP.NET runtime 正在編譯網站。雖然我們的系統有九台機器分擔負載,可是九台都是一起更新,也就是每一台伺服器都同時在重新編譯網站,因此當使用者正在火線作業時,會突然發現網站好像掛掉,持續大約五、六分鐘以後才恢復正常的反應速度。這樣的狀況我想沒有任何一個使用者可以接受,無論你怎麼解釋(如:第一個存取網站的人會比較慢)都很難平息他們的怒火。
2. 解法
結果是在 web.config 裡面加上這段就解決了:
<compilation debug="false" batch="true" maxBatchGeneratedFileSize="10000"
maxBatchSize="10000">
就這麼一行,速度差別竟然這麼大!怎麼回事?
當然我們也有調整別的地方,但是影響速度最大的還是前面那行設定。這主要和 ASP.NET 網站的編譯模型有關;由於我們的網站檔案數量龐大,因此每次網站重新編譯時,都會產生為數眾多的 DLL 檔。你可以參考 Dino Esposito 在 MSDN Magazine 的 Cutting Edge 專欄發表的文章:The Server Side of ASP.NET Pages。從這篇文章你可以了解 ASP.NET 編譯網站時會產生哪些暫存檔案。
文章裡面提到一個跟編譯模型有關的 web.config 元素:
當然囉,DLL 變大了,在記憶體的運用方面就沒有零碎檔案這麼靈活,但我們的機器有很充足的記憶體,用空間換取時間,很划算。
就像大部分的效能調校過程一樣,我們並不是第一次就找到癥結點。我們曾懷疑:
- 硬體等級太差(這點很快就排除了)
- 某些元件採用 J# 撰寫,造成 CLR 的負擔
- 可能是 production 環境裡面包含許多 Subversion 版本控制檔案的緣故,增加不少編譯時間
在我參與過的 multi-tier 架構的專案裡(不多,十個指頭數得出來),大部分都曾出現過效能問題。開發人員碰到這類問題時,往往不知從何下手,常見的反應是來個亂槍打鳥:這邊調一下 SQL、那邊資料表加個索引等等(大部分還真管用),如果都沒效,就擴充 CPU 和 RAM,先撐個一陣子再說。
效能調教方面的知識和技能,我覺得有點像保險,經常是沒碰到的時候嫌太多,需要的時候嫌太少。如果學習新技術時能夠盡量往底層挖,打好了硬底子,將來碰到效能問題時,會有更多 idea 和工具,知道有哪些面向要考量,定出效能調校的方向和策略。方向抓對了,問題可以說已經解決一半了。
說到這個,我想到以前開發過的一個專案,用 Java/JSP 寫的;客戶在台中,開發小組在台北。系統上線後,使用者跳腳:「按鈕按下去竟然要等四、五分鐘才跑出結果!」我聽了很訝異,在公司的測試環境怎麼測都只要幾十秒就完成了啊。由於客戶在台中,我也不可能隨時想到什麼 idea 就跑去客戶端測試,得採取決勝千里外的作法才行。因此,我在程式裡埋了一些測試效能的機制,說穿了很簡單:前端網頁按鈕按下時,用 JavaScript 在網頁的隱藏欄位裡記錄 request 發送的時間(request_send_time),後端 Java servlet 則會紀錄收到 request 的時間(request_receive_time),即將傳回 response 之前也會紀錄一次時間(response_return_time),這些時間變數都會一併傳回用戶端頁面,待用戶端收到 response 時,在前端網頁的 onLoad 事件中紀錄一次時間(response_reseive_time)。最後,在一個獨立的除錯頁框中顯示這幾個時間,就可以清楚看出幾個關鍵的時間區間:
- 從前端發出 request 開始到 server 收到的過程中花了多少時間。如果這段時間很長,那麼問題很可能出在網路傳輸。
- 後端 server 的處理時間。如果這段時間很長,再朝向資料庫設計、調 SQL、調程式碼的方向進一步追查。
- 後端 server 傳回 request 開始到前端網頁載入完畢所花的時間。如果這段時間很長,那麼問題很可能出在網路傳輸。
小結
談笑間,系統風馳電掣--這大概每個效能調校人員的夢想吧。不過,如果沒有平日不斷學習技術和累積經驗,每次碰到效能問題時恐怕還是容易流於亂槍打鳥。