狀態管理本來是一件很美好的事情,嘿嘿,只可惜總是有些廠商在實現的時候考慮得不那么周全。例如MS在ASP中的狀態管理實現就比較爛,因為只實現了一個進程內的基于內存的狀態管理,故而存在很多問題:
1.所有的Session數據都保存在Web服務的進程中,會造成服務器支持會話數量受到服務器內存資源的限制問題,同時也因為大量非活動會話導致內存被無效占用。
2.服務器進程崩潰會導致所有的會話數據丟失。
3.會話無法跨進程或在負載均衡情況下使用,除非負載均衡技術保障同一用戶每次都能被路由到同一機器上。就算這樣也無法保障服務器崩潰造成的會話數據丟失。
4.需要Cookie的支持,而現在因為安全性問題,很多人在瀏覽器中關閉了Cookie和js的支持。
為此ASP的使用者不得不自己手工將會話信息以會話ID為主鍵同步到外部數據庫中,以緩解類似問題。
而在ASP.NET中,因為設計時就考慮了這些問題,能夠避免這些限制:
1.支持進程外的狀態管理,通過獨立狀態管理服務或SQLServer狀態服務器管理會話狀態
2.支持不使用Cookie的狀態維護,通過在URL中自動增加會話ID來避免使用Cookie
3.通過獨立的狀態管理服務或SQLServer狀態服務器支持負載均衡時同步使用會話信息
實現這些特性的正是上節提到的 SessionStateModule .InitModuleFromConfig函數中,根據 sessionState 標記的mode屬性選擇的四種不同的狀態管理器實現。
|
Off模式禁止會話管理,同時ASP.NET還允許通過在頁面中以EnableSessionState屬性細粒度管理頁面的會話支持狀態
|
InProc模式兼容以前ASP的策略,在ASP.NET同一進程空間內實現基于內存的會話狀態管理,速度最快但受到與ASP相同的限制;
StateServer模式通過ASP.NET獨立安裝的ASP.NETStateService服務(aspnet_state.exe),以stateConnectionString指定的IP和端口響應會話狀態服務;
SQLServer模式則通過sqlConnectionString指定的SQLServer服務器,以內存臨時表(以InstallSqlState.sql建庫,使用tempdb內存數據庫)或獨立表(以InstallPersistSqlState.sql監控,使用獨立的ASPState庫)維護會話狀態。
這四種不同的狀態管理器,在性能上據《PerformanceTuningandOptimizingASP.NETAppliation》一書的測試,相對值如下:
以下為引用:
Table4-1:NormalizedTTLB(TimetoLastByte)bySessionStateMode(inMillisecondsper100Requests)
CONCURRENTBROWSERSMODE=OFFMODE=INPROCMODE=STATESERVERMODE=SQLSERVER
17.814.548.278.47
528.2820.2527.2529.29
1089.3846.0877.2985.11
Table4-2:AverageRequestsperSecondbySessionStateMode
CONCURRENTBROWSERSMODE=OFFMODE=INPROCMODE=STATESERVERMODE=SQLSERVER
118.8624.1718.3118.11
521.6625.7421.5421.34
1017.2323.818.1117.6
可以看到,無論是從TTLB還是每秒平均請求數來說,進程外狀態管理器的性能都是可以令人接受的,當然還需要針對狀態管理情況在編寫代碼時做相關優化。不過要使用進程外狀態管理器,則保存在會話中的對象受到必須提高二進制序列化支持的限制。
從使用角度來看,狀態管理器實際上都是由上節提到的HttpSessionModule建立管理,并通過HttpSessionState接口提供訪問的,結構如下圖:
MSDN上的 UnderpinningsoftheSessionStateImplementationinASP.NET 一文非常詳細的解釋了幾種不同狀態管理器的原理和使用,這兒就不羅嗦了。
從實現角度來看,上節中提到的 SessionStateModule .InitModuleFromConfig函數,根據配置文件中狀態管理器的模式,分別建立System.Web.SessionState.InProcStateClientManager,System.Web.SessionState.OutOfProcStateClientManager和System.Web.SessionState.SqlStateClientManager三類狀態管理器的實例。他們都繼承自System.Web.SessionState.StateClientManager抽象基類,并通過System.Web.SessionState.IStateClientManager接口向HttpApplication提高狀態管理服務。
IStateClientManager接口是狀態管理器的統一管理接口,主要提供以下功能:
|
ConfigInit方法主要在初始化狀態管理器時通知其根據配置進行初始化工作,并將負責會話狀態清除的SessionOnEndTarget對象實例綁定到會話管理器(我們后面討論會話狀態管理實現時詳細討論)。對OutOfProcStateClientManager和SqlStateClientManager來說,在此階段還會初始化與外部服務器的連接,并通過一個System.Web.Util.ResourcePool實例,提供基于時間策略的資源池來維護連接;
ResetTimeout方法重置指定Session的超時時間;對InProcStateClientManager來說,這個超時時間是通過System.Web.Caching.CacheInternal類型實現的緩存對象來使用的;OutOfProcStateClientManager直接通過MakeRequest函數構造請求發給外部獨立的狀態管理器執行;SqlStateClientManager則調用存儲過程TempResetTimeout更新ASPStateTempSessions表的過期時間Expires字段;
Dispose方法是否狀態管理器的資源,落實到代碼就是對OutOfProcStateClientManager和SqlStateClientManager中資源池的釋放;
Set方法則將指定的SessionStateItem存儲到id相關的會話數據中,并根據inStorage指定的對象狀態,決定在發生異常的情況下是否釋放對此會話的鎖。與ResetTimeout的實現類似,OutOfProcStateClientManager發送請求給外部獨立的狀態管理器;SqlStateClientManager調用存儲過程TempUpdateStateItemXXX更新會話狀態表ASPStateTempSessions中的過期時間Expires字段、鎖定狀態Lock字段、以及狀態信息SessionItemShort/SessionItemLong(分別保存7000字節以下或之上的數據)。如發生異常并設置inStorage標記,則先調用TempReleaseStateItemExclusive釋放會話鎖。
對狀態管理器中數據的獲取較為復雜,IStateClientManager接口使用的是異步調用的模式,并為提高效率將獨占的獲取數據單獨拿出來。狀態管理器實現類通過通用基類System.Web.SessionState.StateClientManager實現的幾個工具方法,將數據獲取操作異步化。再最終由實現類通過Get和GetExclusive方法完成操作。獲取數據的方法InProcStateClientManager通過緩存;OutOfProcStateClientManager通過請求;SqlStateClientManager通過TempGetStateItemXXX存儲過程完成。
在了解了 SessionStateModule 控制的狀態服務器的實現和使用方法后,我們來看看上層的HttpSessionState是如何使用的。
MandeepSBhatia的 ASP.NETSessionManagementInternals 介紹了HttpSessionState內部完成狀態信息管理的原理。HttpSessionState的Item屬性實際上是通過SessionDictionary實例實現的。
|
而此SessionDictionary實例與HttpSessionState實例的構造,都是在前面提到的完成會話構造的 SessionStateModule .CompleteAcquireState方法中完成的:
|
這兒涉及到的幾個字段,基本上都能跟HttpSessionState提供的公共屬性對應起來。需要注意的是HttpSessionState.StaticObjects是通過ASP.NET頁面上的<objectRunat="Server"Scope="Session"/>類似標記靜態定義的;_rqReadonly則是前面提到的<%@PageEnableSessionState="ReadOnly"%>標記設置的。
至此,狀態管理器的使用與實現方法基本上分析完成,下面整理一下其使用流程:
1.構造:HttpApplication在初始化過程中調用InitModules初始化配置文件Machine.config中注冊的實現了IHttpModule接口的HTTP模塊;其中 SessionStateModule 作為模塊之一被構造并初始化;其InitModuleFromConfig方法根據配置文件中狀態管理器的相關配置,構造并初始化相應的狀態管理器;并根據各種條件調用CompleteAcquireState方法完成HttpSessionState的構造工作。
2.使用:HttpSessionState通過SessionDictionary實現其Item屬性的狀態數據管理;SessionDictionary本身由 SessionStateModule .OnReleaseState在適當的時候寫回狀態管理器;其他維護操作也是通過 SessionStateModule 調用狀態管理器的IStateClientManager接口完成的。
3.實現:狀態管理器從抽象基類StateClientManager獲得異步調用的封裝;通過IStateClientManager接口提供給 SessionStateModule 管理其初始化、釋放和管理的接口。
雖然ASP.NET做了很多工作,但個人感覺還遠遠不夠。例如InProc/OutOfProc實際上都是在內存中,只是解決了一個可靠性和數據集中同步的問題;SQLServer雖然能夠解決容量、可靠性和數據集中同步的問題,但效率又受到影響。這方面.NET應該向Java好好學習一下,例如Java下 EHCache 和 OSCache 都提供了平滑的可配置二級(內存/硬盤)緩存介質切換,并且后者還提供了對負載均衡的簡單支持,此外還有JBoss等實現的基于IP多播等實現技術的負載均衡緩存實現等等,都遠遠超出了ASP.NET提供的緩存機制所考慮到的范圍。雖然ASP.NET也有獨立的緩存機制,MS也提出了CacheApplicationBlock的參考實現,不過還是任重而道遠啊,呵呵
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
