趁著假日對一台ASP.NET MVC網站進行長時間壓測,初期數據表現不俗,顯示調校策略奏效,放著讓程式跑測試穩定性。中午因事外出,回家後馬上檢查系統是否穩定,登楞! 測試畫面顯示Web Application已重啟… orz
猜想是程式寫法有問題導致Crash,心頭涼了半截,沮喪地檢查IIS主機的事件檢視器,想找出ASP.NET錯誤或IIS Crash的線索,卻發現以下記錄:
A worker process with process id of 'xxx' service application pool 'zzz' has requested a recyle because the worker process reached its allowed processing time limit.
因為工作者處理序已達到允許的處理時間上限,所以伺服應用程式集區 'zzz' 且處理序識別碼為 'xxx' 的工作者處理序已要求回收。
哈! 原來不是程式當掉,而是Application Pool(應用程式集區)被IIS強制回收導致重啟。一則以喜,一則以憂,喜的是程式沒錯,不需要抓Bug ^_^;憂的是自己的IIS經驗不及格,無法一眼看出這是什麼妖魔鬼怪? orz
抱著慚愧的心情查文件,整理筆記如下:
從IIS6起,就有定期重啟Application Pool機制,以解決程式跑久可能出現記憶體洩漏(Memory Leaking,指記憶體用完沒歸還,導致可用記憶體愈來愈少)或其他稀奇古怪的問題,這類疑難雜症通常在重新啟動程序後就會一掃而空。但,這是那門子鴕鳥心態? 程式沒寫好不是該徹底抓漏除盡一切Bug嗎? 靠重開逃避問題算什麼男子漢?
的確,天下沒有抓不到的Bug,端看你願意付出多少心血跟它對抗,無奈人生苦短、老闆/客戶耐心有限! 花上半年反覆實驗,靠奇技淫巧解開程式持續跑三天當機之謎,其興奮度勝過拿下NBA總冠軍,這點我絕對相信。但是,若放任正式系統不穩超過一星期,開發團隊早已萬箭穿心。更何況機房、系統裡常存在人類至今無法理解的神祕力量(不然機房也不會有那麼多"乖乖傳奇"),如果3R(Reset、Restart、Reinstall)可以立刻解決又不傷身體,何苦跟自己過不去? 要追根究底不是不行,把戰場拉回實驗室再談成長學習,正式環境還是得求快速解決問題。
於是,IIS有定期回收(Recyling)機制,預設每29小時(1740分鐘)回收Worker Process。莫非我就是中了IIS 29小時魔咒? 由IIS Log證實了這點:
由回收時間4/4 12:50向前推29小時,為4/3 07:50,換成UTC時間為4/2 23:50,Bingo!!
任意回收Worker Process,難道不會影響線上服務? IIS設想周到,回收前會先悄悄另起新的Worker Process,開始接收Request,現有Process則會等到進行中的Request完成再下線,達到無縫接軌的效果(稱為Overlapped Recycle)。如果網站程式被設計成Stateless(無狀態,指IIS主機本身不保存任何狀態資料),切換過程如同Web Farm裡換一台主機連線,使用者是完全無感的。但我的專案並非Stateless,一旦切換主機,儲存於記憶體中的資料遺失,前端就必須重新初始化才能繼續運作。換句話說,如果專案非Stateless,就會遇到每隔29小時程序無故重啟、狀態遺失的魔咒!
幸好在壓測階段發現,等上線後在尖峰時刻爆開,肯定被罵到臭頭!
希望定期重啟維持穩定又不想影響線上服務怎麼辦? 這次的專案受到一些限制,要改成Stateless並非易事,幸好IIS提供另一個選擇 -- 允許管理者自行排定回收Worker Process的時程。剛好系統有每日維護作業時段並對使用者公告,因此在本案例只需將回收作業排在該時段執行就萬事OK。
修改方式如下,透過IIS管理員,找到Appliction Pool,開啟進階設定:
如上圖所示,預設Regular Time Interval(minutes)為1740=29小時
將1740改為0,並在Specific Times加上指定的回收時間即可完成設定。
(除了使用IIS管理員,也可修改ApplicationHost.config <recycling>或使用PowerShell設定。)
【結論】
如果你的網站會因Worker Process重啟導致使用者狀態遺失,務必調整IIS預設的29小時回收設定,以免在尖峰時間發生悲劇。