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

