第三節(jié) MemcachedProviders之SesstionStateProvider(關(guān)于Session的討論)
本節(jié)討論問(wèn)題Memcached緩存有效期及SesstionStateProvider管理Session。
MemcachedProvider是如何控制存儲(chǔ)數(shù)據(jù)的有效期的
一、DefaultExpireTime 和 對(duì)象序列化存儲(chǔ)
配置文件方式
![]() ![]()
在配置文件中可以看到使用了DistCache使用用的緩存策略是MemcachedCacheProvider,如果不需要分布式緩存我們利用WebCache可以繼承CacheProvider重寫一個(gè)緩存Provider注入到Discache中。
keySuffix代表 Key后綴 即給你的Key加后綴,以方便使用MemcachedCacheProvider的不同客戶端區(qū)分各自的緩存數(shù)據(jù)
DefaultExpireTime 緩存數(shù)據(jù)有效期 毫秒為單位,雖然匹配了但默認(rèn)不使用的,必須通過(guò)指定的方法實(shí)現(xiàn)
看一下測(cè)試代碼
![]() ![]()
結(jié)果
![]() 可以看到通過(guò)DistCache.Add("Name2", "小鳳仙我不認(rèn)識(shí)", true); 存儲(chǔ)的數(shù)據(jù)在3秒后再取時(shí)已經(jīng)過(guò)期清除了。
另外我們?cè)谑褂肕emcachedCacheProvider方法存儲(chǔ)對(duì)象時(shí),該對(duì)象一定要標(biāo)記為可序列化Serializable。當(dāng)然你也可以不使用MemcachedCacheProvider,自己先將對(duì)象先序列化再存儲(chǔ),取出來(lái)的時(shí)候自己在反序列化。(沒(méi)有壓縮功能)
既然MemcachedCacheProvider有緩存過(guò)期功能,是不是可以利用這一點(diǎn)+Cookie實(shí)現(xiàn)一個(gè)自定義Session功能呢
當(dāng)用戶登錄了系統(tǒng),這個(gè)時(shí)候生成一個(gè)GUID的PassCode通過(guò)行證做為緩存Key,并將用戶登錄信息保存到緩存中,同時(shí)PassCode根據(jù)請(qǐng)求輸出到客戶端Cookie中保存。此時(shí)Cookie也設(shè)置了有效期,服務(wù)端緩存也設(shè)置了有效期,因此可通過(guò)這兩種情況來(lái)驗(yàn)證用戶是否登錄過(guò)期。
當(dāng)Cookie中PassCode未過(guò)期,服務(wù)端可以從Cookie中獲取PassCode 去緩存中讀取用戶登錄數(shù)據(jù),如果用戶登錄數(shù)據(jù)有效,刷重新設(shè)置用戶數(shù)據(jù)有效期,并充許執(zhí)行相關(guān)操作。當(dāng)用戶數(shù)據(jù)過(guò)期時(shí)則認(rèn)為用戶登錄過(guò)期需重新登錄。
當(dāng)Cookie中PassCode過(guò)期,則服務(wù)端無(wú)法讀取PassCode,則認(rèn)為用戶登錄過(guò)期。 看一下服務(wù)端示例代碼,因?yàn)榭蛻舳酥灰袛嘁幌翽assCode是否存在就可以了。
![]() ![]()
上面處理實(shí)現(xiàn)Session會(huì)話(不是ASP.NET的Session)的優(yōu)點(diǎn):
1.解決跨域,跨窗口的會(huì)話認(rèn)證(針對(duì)ASP.NET的Session不跨窗口),解決A站點(diǎn)轉(zhuǎn)B站點(diǎn)或重新打開(kāi)瀏覽器訪問(wèn)的Session認(rèn)證問(wèn)題。
2.分布式緩存Session或PassCode,實(shí)現(xiàn)認(rèn)證服務(wù)器負(fù)載均橫。
3.效率要高于數(shù)據(jù)庫(kù)。
缺點(diǎn):
1.一臺(tái)緩存服務(wù)器Down掉后,該服務(wù)器上所有Session或PassCode丟失,導(dǎo)致用戶訪問(wèn)過(guò)期。
2.受分布式緩存讀取數(shù)據(jù)命中率影響,一旦未命中則導(dǎo)致用戶訪問(wèn)過(guò)期。
3.分布式緩存效率要低于傳統(tǒng)緩存。
4.Cookie欺騙,偽造在有效期內(nèi)的PassCode去騙取服務(wù)器認(rèn)證。(這個(gè)問(wèn)題無(wú)法避免,即使你用ASP.NET Session)
缺點(diǎn)的第1,2兩條,我們可以通過(guò)重寫MemcachedCacheProvider,增加寫入數(shù)據(jù)庫(kù)功能,同時(shí)這樣做了也會(huì)因反復(fù)讀寫更新數(shù)據(jù)庫(kù)而導(dǎo)致性能降低。當(dāng)用戶Cookie的PassCode還在有效期內(nèi),通過(guò)PassCode去找查緩存服務(wù)器中的用戶登錄信息,如果未查到(數(shù)據(jù)已過(guò)期或未命中亦或服務(wù)器Down掉),則去數(shù)據(jù)庫(kù)中查找并確認(rèn)是否已過(guò)期,如果未過(guò)期則讀取并寫入緩存中(針對(duì)服務(wù)器Down掉和丟失兩種情況)。第3條是沒(méi)有辦法,實(shí)現(xiàn)分布式總是損失部分性能的。至于第4條可以通過(guò)加密解密的方式處理。 個(gè)人覺(jué)得在不是特別的情況下沒(méi)必要增加數(shù)據(jù)庫(kù)備份功能,因?yàn)槟阋磸?fù)讀寫更新數(shù)據(jù)庫(kù)中這個(gè)緩存的有效期,以及過(guò)期后刪除等操作。如果一定要保證Session不丟失,那建議還是直接保存數(shù)據(jù)庫(kù)中,并在每次操作時(shí)更新數(shù)據(jù)的有效期。
Cookies跨域:正常的cookie只能在一個(gè)應(yīng)用中共享,即一個(gè)cookie只能由創(chuàng)建它的應(yīng)用獲得。關(guān)于如何跨域讀取Cookie方法很多,這里不介紹了。
URL后綴跨域:可將PassCode或SessionID通過(guò)URL后綴轉(zhuǎn)到其它站點(diǎn)。(相比Cookie就暴露了,雖然可以加密彌補(bǔ)但是還是無(wú)法防止別人借用)
下面來(lái)看一下Memcahed實(shí)現(xiàn)Asp.net的Session緩存功能,這個(gè)是即實(shí)現(xiàn)了寫入緩存中,又寫入數(shù)據(jù)庫(kù)中并實(shí)時(shí)更新有效期。
二、SesstionStateProvider
Session是由應(yīng)用服務(wù)器維持的一個(gè)服務(wù)器端的存儲(chǔ)空間,用戶在連接服務(wù)器時(shí),會(huì)由服務(wù)器生成一個(gè)唯一的SessionID,用該SessionID 為標(biāo)識(shí)符來(lái)存取服務(wù)器端的Session存儲(chǔ)空間。而SessionID這一數(shù)據(jù)則是保存到客戶端,用Cookie保存的,用戶提交頁(yè)面時(shí),會(huì)將這一 SessionID提交到服務(wù)器端,來(lái)存取Session數(shù)據(jù)。這一過(guò)程,是不用開(kāi)發(fā)人員干預(yù)的。所以一旦客戶端禁用Cookie,那么Session可能會(huì)失效,也可能會(huì)傳Url傳遞即xxx.aspx?SessionID=xxxxxxxxx的形式。
這么看來(lái),Session的原理其實(shí)和我們前面的設(shè)計(jì)的Cookie+PassCode+緩存功能是一致的。即打開(kāi)瀏覽器請(qǐng)求后會(huì)產(chǎn)生一個(gè)Session會(huì)話,該會(huì)話有一個(gè)唯一標(biāo)識(shí)SessionID(相當(dāng)于PassCode),當(dāng)用戶登錄后成功后,將用戶登錄信息緩存至對(duì)應(yīng)用SessionID的Session存儲(chǔ)中(相當(dāng)于緩存)。當(dāng)響應(yīng)返回客戶端后,SessionID被保存在Cookie中。當(dāng)下一次請(qǐng)求回來(lái)時(shí),服務(wù)端會(huì)根據(jù)Cookie中的SessionID從Session存儲(chǔ)中取出對(duì)應(yīng)的Session,綁到上下文中。然后從上下文中的Session獲取相應(yīng)的用戶信息。
既然原理相同,那不可避免的都會(huì)碰到同樣類似的問(wèn)題:
1.Session丟失
2.Cookie或URL后綴參數(shù)SessionID一樣可以欺騙
3.跨站點(diǎn),跨瀏覽器,跨窗口等等訪問(wèn)一樣無(wú)法通行。
有人說(shuō)利用Cookie+Session ,即Session存儲(chǔ)的用戶信息及其它可能更多的數(shù)據(jù)保存到Cookie中。
1.在Cookie有效期內(nèi)Session丟失了,即從Cookie中取回所有數(shù)據(jù)綁回HttpContext的Session中。
2.跨站點(diǎn),跨瀏覽器,跨窗口時(shí),在Cookie有效期內(nèi)將Session綁至其它站點(diǎn)。
3.加密解決欺騙問(wèn)題
這個(gè)時(shí)候Cookie除了傳遞認(rèn)證標(biāo)識(shí),還兼具了彌補(bǔ)Session丟失和跨域的一個(gè)存儲(chǔ)載體了,即取代了Session備份數(shù)據(jù)庫(kù)的功能。那誰(shuí)主導(dǎo)數(shù)據(jù)有效期控制呢?Session本身過(guò)期了,而Cookie沒(méi)有過(guò)期,這樣導(dǎo)致了客戶端一直有權(quán)限訪問(wèn)站點(diǎn)。并且大量的信息被保存在客戶端中,雖然加密了,但仍可借用騙取通行。因此不提倡這種做法,Cookie本身是輕量型的,最好只擔(dān)當(dāng)SessionID或PassCode等標(biāo)識(shí)或一些客戶端自定義性的數(shù)據(jù)的保存和傳遞工作。所歸根到底,關(guān)于會(huì)話憑據(jù)及會(huì)話信息的備份工作是由誰(shuí)來(lái)做?無(wú)疑這一塊還是交給數(shù)據(jù)庫(kù)來(lái)做最放心了,但實(shí)時(shí)的驗(yàn)證工作還是交給緩存或Session存儲(chǔ)本身來(lái)處理。
當(dāng)然你可以選擇使用Cookie+asp.net Session+SQL備份兼跨域,或直接使用Cookie+PassCode+分布式緩存跨域+SQL備份的方案。
甚至也可以兩者接合或 Cookie+asp.net Session+分布式緩存跨域+SQL備份。終于扯到了下面的內(nèi)容了:
Asp.net 本身提供了sessionState的策略注入方案,看一下web.config的配置文件
![]() ![]()
通過(guò)繼承SessionStateStoreProviderBase重寫Provider可以實(shí)現(xiàn)對(duì)Session的進(jìn)一步處理。即HttpApplication管線進(jìn)入HttpHandler的時(shí)候,SessionStateStoreProviderBase會(huì)重新創(chuàng)建Session綁定到HttpContext中,并根據(jù)SessionID從對(duì)應(yīng)的存儲(chǔ)體讀取數(shù)據(jù)初始化這個(gè)Session,當(dāng)HttpHandler結(jié)束請(qǐng)求時(shí),又會(huì)調(diào)用SessionStateStoreProviderBase將從HttpContext中讀取新進(jìn)Session的數(shù)據(jù)保存到對(duì)應(yīng)的存儲(chǔ)體(字典集合,緩存,數(shù)據(jù)庫(kù),分布式緩存等),并且清除掉過(guò)期的數(shù)據(jù),至此Session清除,整個(gè)過(guò)程是循環(huán)往復(fù)的,也就是說(shuō)Session只存在HttpHandler請(qǐng)求期間。
MemcachedProviders/SessionStateProvider在HttpHandler處理請(qǐng)求結(jié)束前,從HttpContext讀取Session并寫入或更新到Memcahed緩存服務(wù)器中,同時(shí)清除掉Memcahed中過(guò)期的數(shù)據(jù),同時(shí)也觸發(fā)了數(shù)據(jù)庫(kù)相應(yīng)的操作(更新緩存有效期,刪除過(guò)期數(shù)據(jù))。當(dāng)下次請(qǐng)求至HttpHandler階段,Memcached又根SessionID讀取緩存中的Session綁回到HttpContext中。所以我們從HttpContext讀取的Session,都是從Memcahed緩存中讀取的。
可以看到providers的節(jié)點(diǎn)中MemcachedSessionProvider的屬性connectionStringName="SqlSessionServices" 指向了一個(gè)數(shù)據(jù)庫(kù)連接。
![]() ![]()
MemcachedProvider提供該數(shù)據(jù)庫(kù)的腳本,一張表tblSessions和相關(guān)操作存儲(chǔ)過(guò)程
![]() proc_CleanExpiredData是清除過(guò)期數(shù)據(jù)的存儲(chǔ)過(guò)程。由于過(guò)期的數(shù)據(jù)或者直接關(guān)閉瀏覽器導(dǎo)致Session沒(méi)有清除,而MemcachedSessionProvider也沒(méi)有自動(dòng)清除這些Session,所以只能通過(guò)調(diào)用此存儲(chǔ)過(guò)程定期清除掉一些過(guò)期的會(huì)話。我們也可以通過(guò)SQL代理實(shí)現(xiàn)這一功能。
看一下Web訪問(wèn)之后的數(shù)據(jù)表結(jié)果
![]() 當(dāng)同一個(gè)Session會(huì)話持續(xù)不斷,Expires和LockDate會(huì)不斷的刷新,以防止過(guò)期。Timeout為默認(rèn)或設(shè)置的過(guò)期時(shí)間(分鐘)。
規(guī)則是這樣,當(dāng)存儲(chǔ)的數(shù)據(jù)過(guò)了有效期后,當(dāng)有訪問(wèn)這些數(shù)據(jù)發(fā)生時(shí),則MemcahedSesstionProvider會(huì)從表里清除這條對(duì)應(yīng)的SessionID記錄。同時(shí)系統(tǒng)彈出登錄過(guò)期,要求用戶重新登錄,這個(gè)時(shí)候只要用戶不是關(guān)閉瀏覽器登錄進(jìn)來(lái)的,還是會(huì)用之前的SessionID再寫入tblSessions表中。關(guān)閉瀏覽器IE會(huì)自動(dòng)清除Cookie上的SessionID,所以這個(gè)時(shí)候再打開(kāi)瀏覽器就新產(chǎn)生了一個(gè)SessionID。通過(guò)SessionStateProvider在Memcahed中保存了Session,同時(shí)也會(huì)實(shí)時(shí)更新至數(shù)據(jù)庫(kù)。
既然Session都寫入數(shù)據(jù)庫(kù)了,完全可以在跨域時(shí)或者丟失從數(shù)據(jù)庫(kù)里讀取在有效期內(nèi)的Session,而沒(méi)必要通過(guò)分布式緩存讀取。當(dāng)不是跨域時(shí)訪問(wèn)時(shí),又可以直接利用Session存儲(chǔ)而沒(méi)有必要通過(guò)Memcached讀取。(以上是誤解)事實(shí)是這樣的嗎? 通過(guò)對(duì)SessionStateProvider源碼分析,SessionStateProvider繼承重寫了SessionStateStoreProviderBase,使用Memcached已經(jīng)替代了原先Session的存儲(chǔ)機(jī)制,而數(shù)據(jù)庫(kù)則是充當(dāng)Memcahed的備份。SessionStateProvider并沒(méi)有畫蛇添足,并且確實(shí)考慮了所有情況,確實(shí)是個(gè)很完美的方案。
相反另一個(gè)問(wèn)題就來(lái)了,為什么說(shuō)Session會(huì)丟失,Session是在什么情況下丟失的?高負(fù)載,高并發(fā)?我沒(méi)有碰到這些情況,假如這些情況容易發(fā)生,相信放到Memcached中也會(huì)碰到這種問(wèn)題,如果是這樣那在不注重效率的情況下,看來(lái)只有直接利數(shù)據(jù)庫(kù)存儲(chǔ)Session是最安全的了。web.config SessionState默認(rèn)支持存入數(shù)據(jù)庫(kù)存中的,只要配置一下就可以了。
![]() ![]()
另外微軟也提供了一種會(huì)話分布式緩存解決方案:
Windows Server AppFabric 為 ASP.NET Web 應(yīng)用程序提供了自定義的會(huì)話狀態(tài)提供程序,Web 應(yīng)用程序可以分散緩存群集中的會(huì)話對(duì)象,從而提供可伸縮性。鑒于 AppFabric 緩存功能的性質(zhì),您放入會(huì)話中的對(duì)象必須可序列化。有時(shí)間在寫一篇關(guān)于Windows Server AppFabric。 |
|
來(lái)自: zww_blog > 《.net技術(shù)》