日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

MemcachedProviders之SesstionStateProvider

 zww_blog 2014-01-17
第三節(jié) MemcachedProviders之SesstionStateProvider(關(guān)于Session的討論)
 
本節(jié)討論問題Memcached緩存有效期及SesstionStateProvider管理Session。
  • DefaultExpireTime 和 對象序列化存儲
  • SesstionStateProvider
MemcachedProvider是如何控制存儲數(shù)據(jù)的有效期的
一、DefaultExpireTime 和 對象序列化存儲
配置文件方式
復(fù)制代碼
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<section name="cacheProvider" type="MemcachedProviders.Cache.CacheProviderSection, MemcachedProviders"
allowDefinition
="MachineToApplication" restartOnExternalChanges="true"/>
<sectionGroup name="enyim.com">
<section name="memcached" type="Enyim.Caching.Configuration.MemcachedClientSection, Enyim.Caching" />
</sectionGroup>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net"/>
</configSections>
<enyim.com>
<memcached>
<servers>
<!-- put your own server(s) here-->
<add address="127.0.0.1" port="11211" />
</servers>
<socketPool minPoolSize="10" maxPoolSize="100"
connectionTimeout
="00:00:10" deadTimeout="00:02:00" />
</memcached>
</enyim.com>
<cacheProvider defaultProvider="MemcachedCacheProvider">
<providers>
<add name="MemcachedCacheProvider"
type
="MemcachedProviders.Cache.MemcachedCacheProvider, MemcachedProviders" keySuffix="_MySuffix_" defaultExpireTime="2000"/>
</providers>
</cacheProvider>
<log4net>
<!-- Define some output appenders -->
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date [%thread] %-5level %logger [%property{NDC}] - %message%newline" />
</layout>
</appender>
<!--<threshold value="OFF" />-->
<!-- Setup the root category, add the appenders and set the default priority -->
<root>
<priority value="WARN"/>
<appender-ref ref="ConsoleAppender">
<filter type="log4net.Filter.LevelRangeFilter">
<levelMin value="WARN"/>
<levelMax value="FATAL"/>
</filter>
</appender-ref>
</root>
</log4net>
</configuration>
復(fù)制代碼
在配置文件中可以看到使用了DistCache使用用的緩存策略是MemcachedCacheProvider,如果不需要分布式緩存我們利用WebCache可以繼承CacheProvider重寫一個(gè)緩存Provider注入到Discache中。
keySuffix代表 Key后綴  即給你的Key加后綴,以方便使用MemcachedCacheProvider的不同客戶端區(qū)分各自的緩存數(shù)據(jù)
DefaultExpireTime 緩存數(shù)據(jù)有效期 毫秒為單位,雖然匹配了但默認(rèn)不使用的,必須通過指定的方法實(shí)現(xiàn)
看一下測試代碼
復(fù)制代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MemcachedProviders.Cache;
using MemcachedProviders.Session;
using System.Threading;

namespace DemoTest
{
class Program
{
static void Main(string[] args)
{
//服務(wù)端緩存通行證數(shù)據(jù)10秒
DistCache.DefaultExpireTime = 10000;

//假如用戶登錄成功
User user = new User();
user.Name
= "小憐香";
user.Age
= 20;
user.Birthday
= Convert.ToDateTime("1991-1-1");
user.Sex
= false;
user.IsLoginSucess
= true;

//生成通過證
string passCode = Guid.NewGuid().ToString();
Console.WriteLine(
"首次緩存數(shù)據(jù)-------------------8秒后讀取");
//緩存用戶信息(10秒) ,并將PassCode保存到Cookie中
DistCache.Add(passCode, user, true);

//如何讓用戶在操作期間 緩存不過期呢?
Thread.Sleep(8000);
Console.WriteLine(DistCache.Get
<User>(passCode).Name);

Console.WriteLine(
"重新緩存--------------9秒后再讀取");
//從Cookie中獲取PassCode ,通過PassCode從緩存中讀取用戶數(shù)據(jù)后重新緩存
//User newUser = DistCache.Get<User>(passCode);
//DistCache.Remove(passCode);
DistCache.Add(passCode, user, DistCache.DefaultExpireTime); //Set方式重新緩存 (MemcacheProvider封裝不是很好)用第一方式設(shè)置還是Add


Thread.Sleep(
8000); //如果上面的代碼沒有刷新緩存至10秒 那下面的代碼肯定會超時(shí)
if(DistCache.Get<User>(passCode)!=null)
Console.WriteLine(DistCache.Get
<User>(passCode).Name);
else
Console.WriteLine(
"因?yàn)闆]有操作,緩存未刷新,導(dǎo)致數(shù)據(jù)過期");

Console.WriteLine(
"3秒后再次讀取數(shù)據(jù)----------");
Thread.Sleep(
3000);
if (DistCache.Get<User>(passCode) != null)
Console.WriteLine(DistCache.Get
<User>(passCode).Name);
else
Console.WriteLine(
"因?yàn)闆]有操作,緩存未刷新,導(dǎo)致數(shù)據(jù)過期");

//以上服務(wù)端的過程 即服務(wù)端用戶登錄數(shù)據(jù)先過期,PassCode取不到用戶登錄數(shù)據(jù),可以判定為登錄過期
//還有一種情況客戶端Cookie先過期 導(dǎo)致無法取到PassCode,因此服務(wù)端就無法獲取用戶數(shù)據(jù),可以判定為登錄過期。
Console.ReadLine();
}

[Serializable]
public class User
{
public string Name { set; get; }
public bool Sex { set; get; }
public int Age { set; get; }
public DateTime Birthday { set; get; }
public bool IsLoginSucess { set; get; }
}

}
}
復(fù)制代碼
結(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是否存在就可以了。
復(fù)制代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MemcachedProviders.Cache;
using MemcachedProviders.Session;
using System.Threading;

namespace DemoTest
{
class Program
{
static void Main(string[] args)
{
//服務(wù)端緩存通行證數(shù)據(jù)10秒
DistCache.DefaultExpireTime = 10000;

//假如用戶登錄成功
User user = new User();
user.Name
= "小憐香";
user.Age
= 20;
user.Birthday
= Convert.ToDateTime("1991-1-1");
user.Sex
= false;
user.IsLoginSucess
= true;

//生成通過證
string passCode = Guid.NewGuid().ToString();
Console.WriteLine(
"首次緩存數(shù)據(jù)-------------------9秒后讀取");
//緩存用戶信息(10秒) ,并將PassCode保存到Cookie中
DistCache.Add(passCode, user, true);

//如何讓用戶在操作期間 緩存不過期呢?當(dāng)用戶每一次操作或訪問就刷新一下服務(wù)端的緩存
Thread.Sleep(9000);
Console.WriteLine(DistCache.Get
<User>(passCode).Name);

Console.WriteLine(
"取出數(shù)據(jù)重新緩存--------------9秒后再讀取");
//從Cookie中獲取PassCode ,通過PassCode從緩存中讀取用戶數(shù)據(jù)后重新緩存
User newUser = DistCache.Get<User>(passCode);
DistCache.Remove(passCode);
DistCache.Add(passCode, newUser,
true);

Thread.Sleep(
9000); //如果上面的代碼沒有刷新緩存至10秒 那下面的代碼肯定會超時(shí)
if(DistCache.Get<User>(passCode)!=null)
Console.WriteLine(DistCache.Get
<User>(passCode).Name);
else
Console.WriteLine(
"因?yàn)闆]有操作,緩存未刷新,導(dǎo)致數(shù)據(jù)過期");

Console.WriteLine(
"持續(xù)11秒沒有刷新操作,再次讀取數(shù)據(jù)----------");
Thread.Sleep(
2000);
if (DistCache.Get<User>(passCode) != null)
Console.WriteLine(DistCache.Get
<User>(passCode).Name);
else
Console.WriteLine(
"因?yàn)闆]有操作,緩存未刷新,導(dǎo)致數(shù)據(jù)過期");

//以上服務(wù)端的過程 即服務(wù)端用戶登錄數(shù)據(jù)先過期,PassCode取不到用戶登錄數(shù)據(jù),可以判定為登錄過期
//還有一種情況客戶端Cookie先過期 導(dǎo)致無法取到PassCode,因此服務(wù)端就無法獲取用戶數(shù)據(jù),可以判定為登錄過期。
Console.ReadLine();
}

[Serializable]
public class User
{
public string Name { set; get; }
public bool Sex { set; get; }
public int Age { set; get; }
public DateTime Birthday { set; get; }
public bool IsLoginSucess { set; get; }
}

}
}
復(fù)制代碼
 
上面處理實(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的配置文件
復(fù)制代碼
<system.web>
<sessionState cookieless="false" regenerateExpiredSessionId="true" mode="Custom"
customProvider
="MemcachedSessionProvider">
<providers>
<add name="MemcachedSessionProvider"
type
="MemcachedProviders.Session.SessionStateProvider,MemcachedProviders"
connectionStringName
="SqlSessionServices" dbType="SQL" writeExceptionsToEventLog="false" />
</providers>
</sessionState>
</system.web>
復(fù)制代碼
     
     通過繼承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ù)庫連接。
<connectionStrings>
<add name="SqlSessionServices" connectionString="Data Source= LIULJ2576\SQL2008;Initial Catalog = Session;User Id = sa;Password = sa;"/>
</connectionStrings>
 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ù)庫存中的,只要配置一下就可以了。
<sessionState mode="SQLServer" allowCustomSqlDatabase="true"
sqlConnectionString
="server=.;uid=sa;password=;initial catalog=sd"
cookieless
="false"
timeout
="20">
</sessionState>
另外微軟也提供了一種會話分布式緩存解決方案:
Windows Server AppFabric 為 ASP.NET Web 應(yīng)用程序提供了自定義的會話狀態(tài)提供程序,Web 應(yīng)用程序可以分散緩存群集中的會話對象,從而提供可伸縮性。鑒于 AppFabric 緩存功能的性質(zhì),您放入會話中的對象必須可序列化。有時(shí)間在寫一篇關(guān)于Windows Server AppFabric

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多