最近處理了一個 Session 變數會莫名消失的問題,麻煩的地方在於,這個舊系統在.NET 1.1的環境上活的很愉快,但是努力升級到.NET 2.0之後,在
某些狀況下 Session 變數就會莫名的消失,導致權控子系統判斷為 Session Timeout,而將 user 自動登出系統。(不會留下任何的Event Log / Error Log
)。
遇到這種問題,當然首先要先釐清到底在甚麼狀況下 Session 變數會消失?一開始大家猜測是 ASP.NET 1.1 和 ASP.NET 2.0 的 Session 有
不同的運作機制(升級到 .NET 2.0 之後系統就掛了),這個大方向是對的,但最後發現問題的癥結其實跟 Session 的運作機制無關,Session 變數消失只是一個很糟糕的
副作用罷了 …
經過反覆的測試,最後終於將範圍縮小至「呼叫一個 WebService (撈外部系統的資料)」後,便會造成 Session 變數消失。此時一位資深的同事注意到,由於外部資料可能常常會有變化,因此系統有一個特殊行為是會將 WebService 回傳的資料寫入一個暫存目錄中,而每次呼叫 WebService 前都會
先將整個暫存目錄(及其中的檔案)刪除,而這就是造成 Session 變數消失的關鍵!(跟權控子系統一點關係都沒有)
經過一番 Google,發現這個 Session 變數會消失的問題最早大約在 2005 年就開始被
討論,原因是從ASP.NET 2.0 開始新增的「
Dynamic Compilation」機制,此玩意兒是指 ASP.NET (2.0+) Runtime 會持續監控應用程式的根目錄(Application Root Folder)以及其下所有的子目錄,出於安全性的考量,當這些目錄被
更名或
刪除時(不管是手動修改或是在 runtime 以程式修改),都會造成
Application Domain Restart!(也就是整個網站瞬間 shutdown、重新 compile 之後,再啟動)
當整個 w3wp process 都被砍掉重練了,Session 自然也不能倖免,直接就被刪除重建,其中的變數更是整個屍骨無存,最後導致 User 自動被登出系統。
在瞭解了基本觀念之後,又有資深的同事們提出了幾個更深入探討的方向,包括:
因此接下來又整理出以下各種 scenario,並逐一進行測試(
X表示Session會被刪除重建,範例程式
下載)。在開始進行測試之前,我的預期是,sessionStateMode 主要的差異在於儲存位置的不同(因此影響
scalability),儲存位置的差異對 ASP.NET Session 物件的運作機制應該沒有影響,因此不管選擇將 Session 的資料存在哪裡,在
Application Domain Restart 的時候,應該全部都會被清空吧!(這樣的話開發人員就不需要特別為了不同的 sessionStateMode 而設計不同的 Session 變數管理方法了)
以下為測試結果:
SessionStateMode
Deployment Style | InProc | StateServer | SQL Server |
Copy Web Site (直接將 Source Code 佈署出去) | X | | |
Pre-Compilation (for deployment) | X | | |
Pre-Compilation (for deployment and update)
(等於執行Visual Studio 內建的 Publish WebSite) | X | | |
但是!事情並不是懶惰的我所想的那麼簡單!由以上的測試結果可明確的看出:選擇不同的 sessionStateMode 對 Session 變數會有截然不同的影響!在 sessionStateMode 採用「
InProc Mode」時,只要
更名(或
刪除)網站的子目錄就會造成
Application Domain Restart(新增、刪除檔案則沒有這個副作用),若採用 StateServer/SQL Server 來儲存 Session 資料,則不管 Application Domain 如何反覆的砍掉重練,Session 變數還是可以堅忍不拔的活的很快樂!(其餘的 sessionState 屬性都採用
預設值)
結論:
ASP.NET 2.0+ 的 Dynamic Compilation 行為是無法被改變的,若系統採用「
InProc Mode」來儲存 Session 資料,且想要用程式在 runtime 清除暫存資料可以採取以下兩種作法:
- 以迴圈的方式刪除暫存目錄下的所有檔案,不要把暫存目錄整個刪除,此作法的effort 較小,亦可將暫存目錄設置於網站根目錄下一併管理。
- 將暫存目錄的位置移出網站的根目錄,如此便可任意更名/刪除此暫存目錄,缺點為管理上的 effort 較大(多一個虛擬目錄要管理)。
後記:
我不太懂為何 ASP.NET 會設計成,當採用 StateServer/SQLServer Mode 時就不清空 Session 資料。這樣就表示不同的 sessionStateMode 所代表的不僅僅是 Session 資料的儲存位置不同,連帶的
ASP.NET 對於 Session 資料的管理策略也是不同的,感覺對於開發人員來說這會是一個額外的負擔。
從效能的觀點來看,若系統中有某個功能一旦被執行就會造成
Application Domain Restart,那絕對不是甚麼好事。但是在開發階段只有採用「InProc Mode」或者已經事先深入瞭解 Dynamic Compilation 的運作機制,才能避免效能低落的程式寫法。若是經驗不足,或是不小心採用了 StateServer/SQLServer Mode,那麼系統只會
默默的把 Application Domain 砍掉重練,不會丟出任何警告訊息,也不會寫下 Event Log,而Session 變數的內容看起來都會是很正常的!(權控子系統都可正常運作)那麼效能也只能
默默的變差了 …
為了補足 ASP.NET 預設對於 Application Shutdown 事件不會寫 log 的缺憾,我在Microsoft Developer Division 的 VP - Scott Guthrie 的 blog 找到了
一個方法,在Global.asax的「Application_End」事件中將Application Shutdown的資訊寫入到Event Log,如下圖所示,如此便可明確看出
Application Shutdown 的原因是因為「
dir change or directory rename」:
Reference:
- ASP.NET Application Life Cycle Overview for IIS 5.0 & 6.0
(See Community Content #1)
- Logging ASP.NET Application Shutdown Events
- ASP.NET Deployment Overview
- sessionState Element