什么是redis
redis是一個(gè)開源的使用ANSI c語(yǔ)言編寫、支持網(wǎng)絡(luò)、可基于內(nèi)存亦可持久化的日志型、Key-Value數(shù)據(jù)庫(kù),并提供多種語(yǔ)言的API.
1.與其他用戶狀態(tài)保存方案比較
一般開發(fā)中用戶狀態(tài)使用Session或者Cookie,兩種方式各種利弊。
Session:在InProc模式下容易丟失,并且引起并發(fā)問(wèn)題。如果使用sqlserver或者SQLServer模式又消耗了性能
Cookie則容易將一些用戶信息暴露,加解密同樣也消耗了性能。
Redis采用這樣的方案解決了幾個(gè)問(wèn)題,
1.Redis存取速度快。
2.用戶數(shù)據(jù)不容易丟失。
3.用戶多的情況下容易支持集群。
4.能夠查看在線用戶。
5.能夠?qū)崿F(xiàn)用戶一處登錄。
6.支持持久化。
2.實(shí)現(xiàn)思路
1.我們知道session其實(shí)是在cookie中保存了一個(gè)sessionid,用戶每次訪問(wèn)都將sessionid發(fā)給服務(wù)器,服務(wù)器通過(guò)ID查找用戶對(duì)應(yīng)的狀態(tài)數(shù)據(jù)。
在這里我的處理方式也是在cookie中定義一個(gè)sessionid,程序需要取得用戶狀態(tài)時(shí)將sessionid做為key在Redis中查找。
2.同時(shí)session支持用戶在一定時(shí)間不訪問(wèn)將session回收。
借用Redis中Keys支持過(guò)期時(shí)間的特性支持這個(gè)功能,但是在續(xù)期方面需要程序自行攔截請(qǐng)求調(diào)用這個(gè)方法(demo有例子)
下面開始代碼說(shuō)明
3.Redis調(diào)用接口
首先引用ServiceStack相關(guān)DLL。
在web.config添加配置,這個(gè)配置用來(lái)設(shè)置Redis調(diào)用地址每臺(tái)服務(wù)用【,】隔開。主機(jī)寫在第一位
?<appsettings> ?????<!--每臺(tái)Redis之間用,分割.第一個(gè)必須為主機(jī)--> ????<add></add> ?</appsettings>
初始化配置
static?Managers() ????????{ ????????????string?sessionRedis=?ConfigurationManager.AppSettings["SessionRedis"]; ????????????string?timeOut?=?ConfigurationManager.AppSettings["SessionRedisTimeOut"]; ????????????if?(string.IsNullOrEmpty(sessionRedis)) ????????????{ ????????????????throw?new?Exception("web.config?缺少配置SessionRedis,每臺(tái)Redis之間用,分割.第一個(gè)必須為主機(jī)"); ????????????} ????????????if?(string.IsNullOrEmpty(timeOut)==false) ????????????{ ????????????????TimeOut?=?Convert.ToInt32(timeOut); ????????????} ????????????var?host?=?sessionRedis.Split(char.Parse(",")); ????????????var?writeHost?=?new?string[]?{?host[0]?}; ????????????var?readHosts?=?host.Skip(1).ToArray(); ????????????ClientManagers?=?new?PooledRedisClientManager(writeHost,?readHosts,?new?RedisClientManagerConfig ????????????{ ????????????????MaxWritePoolSize?=?writeReadCount,//“寫”鏈接池鏈接數(shù) ????????????????MaxReadPoolSize?=?writeReadCount,//“讀”鏈接池鏈接數(shù) ????????????????AutoStart?=?true ????????????}); ????????}
為了控制方便寫了一個(gè)委托
?///?<summary> ????????///?寫入 ????????///?</summary> ????????///?<typeparam></typeparam> ????????///?<param> ????????///?<returns></returns> ????????public?F?TryRedisWrite<f>(Func<iredisclient>?doWrite) ????????{ ????????????PooledRedisClientManager?prcm?=?new?Managers().GetClientManagers(); ????????????IRedisClient?client?=?null; ????????????try ????????????{ ????????????????using?(client?=?prcm.GetClient()) ????????????????{ ????????????????????return?doWrite(client); ????????????????} ????????????} ????????????catch?(RedisException) ????????????{ ????????????????throw?new?Exception("Redis寫入異常.Host:"?+?client.Host?+?",Port:"?+?client.Port); ????????????} ????????????finally ????????????{ ????????????????if?(client?!=?null) ????????????????{ ????????????????????client.Dispose(); ????????????????} ????????????} ????????}</iredisclient></f>
?一個(gè)調(diào)用的例子其他的具體看源碼
????????///?<summary> ????????///?以Key/Value的形式存儲(chǔ)對(duì)象到緩存中 ????????///?</summary> ????????///?<typeparam>對(duì)象類別</typeparam> ????????///?<param>要寫入的集合 ????????public?void?KSet(Dictionary<string>?value) ????????{ ????????????Func<iredisclient>?fun?=?(IRedisClient?client)?=> ????????????{ ????????????????client.SetAll<t>(value); ????????????????return?true; ????????????}; ????????????TryRedisWrite(fun); ????????}</t></iredisclient></string>
4.實(shí)現(xiàn)Session
按上面說(shuō)的給cookie寫一個(gè)sessionid
?
????///?<summary> ????///?用戶狀態(tài)管理 ????///?</summary> ????public?class?Session ????{ ????????///?<summary> ????????///?初始化 ????????///?</summary> ????????///?<param> ????????public?Session(HttpContextBase?_context) ????????{ ????????????var?context?=?_context; ????????????var?cookie?=?context.Request.Cookies.Get(SessionName); ????????????if?(cookie?==?null?||?string.IsNullOrEmpty(cookie.Value)) ????????????{ ????????????????SessionId?=?NewGuid(); ????????????????context.Response.Cookies.Add(new?HttpCookie(SessionName,?SessionId)); ????????????????context.Request.Cookies.Add(new?HttpCookie(SessionName,?SessionId)); ????????????} ????????????else ????????????{ ????????????????SessionId?=?cookie.Value; ????????????} ????????} ????}
去存取用戶的方法
????????///?<summary> ????????///?獲取當(dāng)前用戶信息 ????????///?</summary> ????????///?<typeparam></typeparam> ????????///?<returns></returns> ????????public?object?Get<t>()?where?T:class,new() ????????{ ????????????return?new?RedisClient<t>().KGet(SessionId); ????????} ????????///?<summary> ????????///?用戶是否在線 ????????///?</summary> ????????///?<returns></returns> ????????public?bool?IsLogin() ????????{ ????????????return?new?RedisClient<object>().KIsExist(SessionId); ????????} ????????///?<summary> ????????///?登錄 ????????///?</summary> ????????///?<typeparam></typeparam> ????????///?<param> ????????public?void?Login<t>(T?obj)?where?T?:?class,new() ????????{ ????????????new?RedisClient<t>().KSet(SessionId,?obj,?new?TimeSpan(0,?Managers.TimeOut,?0)); ????????}</t></t></object></t></t>
?
?
6.續(xù)期
默認(rèn)用戶沒訪問(wèn)超過(guò)30分鐘注銷用戶的登錄狀態(tài),所以用戶每次訪問(wèn)都要將用戶的注銷時(shí)間推遲30分鐘
這需要調(diào)用Redis的續(xù)期方法
?
??????///?<summary> ????????///?延期 ????????///?</summary> ????????///?<param> ????????///?<param> ????????public?void?KSetEntryIn(string?key,?TimeSpan?expiresTime) ????????{ ????????????Func<iredisclient>?fun?=?(IRedisClient?client)?=> ????????????{ ????????????????client.ExpireEntryIn(key,?expiresTime); ????????????????return?false; ????????????}; ????????????TryRedisWrite(fun); ????????}</iredisclient>
?
封裝以后
///?<summary> ///?續(xù)期 ///?</summary> public?void?Postpone() { new?RedisClient<object>().KSetEntryIn(SessionId,?new?TimeSpan(0,?Managers.TimeOut,?0)); }</object>
?
?
這里我利用了MVC3中的ActionFilter,攔截用戶的所有請(qǐng)求
namespace?Test { ????public?class?SessionFilterAttribute?:?ActionFilterAttribute ????{ ????????///?<summary> ????????///?每次請(qǐng)求都續(xù)期 ????????///?</summary> ????????///?<param> ????????public?override?void?OnActionExecuting(ActionExecutingContext?filterContext) ????????{ ????????????new?Session(filterContext.HttpContext).Postpone(); ????????} ????} }
?
在Global.asax中要注冊(cè)一下
public?static?void?RegisterGlobalFilters(GlobalFilterCollection?filters) ????????{ ????????????filters.Add(new?SessionFilterAttribute()); ????????} ????????protected?void?Application_Start() ????????{ ????????????RegisterGlobalFilters(GlobalFilters.Filters); ????????}
?
5.調(diào)用方式
為了方便調(diào)用借用4.0中的新特性,把Controller添加一個(gè)擴(kuò)展屬性
public?static?class?ExtSessions {public?static?Session?SessionExt(this?Controller?controller) ????{ ????????return?new?Session(controller.HttpContext); ????} }
?
調(diào)用方法
????public?class?HomeController?:?Controller ????{ ????????public?ActionResult?Index() ????????{ ????????????this.SessionExt().IsLogin(); ????????????return?View(); ????????} ????}