趁著假日對一台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小時回收設定,以免在尖峰時間發生悲劇。


Comments

# by Samho

跟本設定了這個之後還是會不穩定, 要都多工作階段解釋 ASP.Net 不穩定的情況, 用 Cookie 記錄所有狀態, 必要時用 ASP.Net Session Server 令多個工作階段可保存狀態即可, 多個工作階段可更順暢運用多核心CPU

# by William Lee

如果網站只服務國內使用者, 回收時間設在凌晨可以減少發生問題的機會; 但如果網站是向面國際, 也就是24小時都有可能來自世界各地的使用者, 沒有明顯離峰或尖峰的時段, 那該如何設定適合的回收時間 (更新系統、重啟系統也面臨同樣的問題)。謝謝

# by Jeffrey

to William Lee, 最理想的解決方案是將網站設計成無狀態並架設多台放在負載平衡設備(Load Balancer)後方,Session 或狀態保存在 Redis 之類的快速 DB,不要依賴個別主機記憶體。做到使用者上一秒連 Server A、下一秒連 Server B 都不會察覺有異。主機要更新、重啟前將其自 Load Balancer 卸除不提供服務,待準備好再重新掛上去。 這要實現有點難度,但要做到 24 小時完全不能中斷,本來就不是件容易事。

# by White

Dark 您好:前面有提到回收前,先將請求丟給新的「工作者處理程序」處理,原有的「工作者處理程序」則是處理完請求後即被回收。但是,後面的結論卻說應用程式集區的回收會導致使用者狀態遺失。由前後者推敲起來,是否使用者的狀態不會交給新的「工作者處理程序」保存呢?

# by Jeffrey

to White, 這裡假設狀態資料只存在程序的記憶體,未使用 Redis、SQL 等獨立服務長期儲存。在此前題下,原工作者處理程序一結束記憶體內容就消失了,不會有移交給新工作者處理程序的動作。

# by White

Dark 您好,感謝您花費時間的回覆。以下有幾個狀況,想跟您請教,請您不吝嗇回覆,謝謝。 1、原工作者處理程序只剩下某一位使用者的狀態資料(連線Session),後續收到的請求將交由新工作者處理程序負責,那麼原工作者處理程序是否會等到使用者的狀態資料在逾期或釋放時結束原工作者處理程序呢?

# by Jeffrey

to White,原工作者處理程序在手上處理到一半的請求做完後就會結束,不會等到使用者狀態逾期。

# by White

Dark 您好,綜上所述,使用者狀態跟工作者處理程序沒有關係?那麼使用者狀態就不會隨原工作者處理程序結束而結束,是這樣嗎?非常抱歉,對這部分不是很懂。

# by Jeffrey

to White,若沒有使用 SQL 或 ASP.NET State Service 之類的服務,使用者狀態(Session)的資料會存在 ASP.NET 的工作者程序(Worker Process)的記憶體中,存在記憶體的資料會在程序結束時消失。所以,當原工作者處理程序結束,之後使用者連上新工作者處理程序,其記憶體已經沒有之前存入的資料,其使用者狀態就遺失了(即使還沒有逾期)。

Post a comment