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