详解用Redis实现Session功能
0.什么是redis
redis是一个开源的使用ansi c语言编写、支持网络、可基于内存亦可持久化的日志型、key-value数据库,并提供多种语言的api
1.与其他用户状态保存方案比较
一般开发中用户状态使用session或者cookie,两种方式各种利弊。
session:在inproc模式下容易丢失,并且引起并发问题。如果使用sqlserver或者sqlserver模式又消耗了性能
cookie则容易将一些用户信息暴露,加解密同样也消耗了性能。
redis采用这样的方案解决了几个问题,
①.redis存取速度快。
②.用户数据不容易丢失。
③.用户多的情况下容易支持集群。
④.能够查看在线用户。
⑤.能够实现用户一处登录。(通过代码实现,后续介绍)
⑥.支持持久化。(当然可能没什么用)
2.实现思路
1.我们知道session其实是在cookie中保存了一个sessionid,用户每次访问都将sessionid发给服务器,服务器通过id查找用户对应的状态数据。
在这里我的处理方式也是在cookie中定义一个sessionid,程序需要取得用户状态时将sessionid做为key在redis中查找。
2.同时session支持用户在一定时间不访问将session回收。
借用redis中keys支持过期时间的特性支持这个功能,但是在续期方面需要程序自行拦截请求调用这个方法(demo有例子)
下面开始代码说明
3.redis调用接口
首先引用servicestack相关dll。
在web.config添加配置,这个配置用来设置redis调用地址每台服务用【,】隔开。主机写在第一位
<appsettings> <!--每台redis之间用,分割.第一个必须为主机--> <add key="sessionredis" value="127.0.0.1:6384,127.0.0.1:6384"/> </appsettings>
初始化配置
static managers() { string sessionredis= configurationmanager.appsettings["sessionredis"]; string timeout = configurationmanager.appsettings["sessionredistimeout"]; if (string.isnullorempty(sessionredis)) { throw new exception("web.config 缺少配置sessionredis,每台redis之间用,分割.第一个必须为主机"); } 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,//“写”链接池链接数 maxreadpoolsize = writereadcount,//“读”链接池链接数 autostart = true }); }
为了控制方便写了一个委托
/// <summary> /// 写入 /// </summary> /// <typeparam name="f"></typeparam> /// <param name="dowrite"></param> /// <returns></returns> public f tryrediswrite<f>(func<iredisclient, f> 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(); } } }
一个调用的例子其他的具体看源码
/// <summary> /// 以key/value的形式存储对象到缓存中 /// </summary> /// <typeparam name="t">对象类别</typeparam> /// <param name="value">要写入的集合</param> public void kset(dictionary<string, t> value) { func<iredisclient, bool> fun = (iredisclient client) => { client.setall<t>(value); return true; }; tryrediswrite(fun); }
4.实现session
按上面说的给cookie写一个sessionid
/// <summary> /// 用户状态管理 /// </summary> public class session { /// <summary> /// 初始化 /// </summary> /// <param name="_context"></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> /// 获取当前用户信息 /// </summary> /// <typeparam name="t"></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 name="t"></typeparam> /// <param name="obj"></param> public void login<t>(t obj) where t : class,new() { new redisclient<t>().kset(sessionid, obj, new timespan(0, managers.timeout, 0)); }
6.续期
默认用户没访问超过30分钟注销用户的登录状态,所以用户每次访问都要将用户的注销时间推迟30分钟
这需要调用redis的续期方法
/// <summary> /// 延期 /// </summary> /// <param name="key"></param> /// <param name="expirestime"></param> public void ksetentryin(string key, timespan expirestime) { func<iredisclient, bool> fun = (iredisclient client) => { client.expireentryin(key, expirestime); return false; }; tryrediswrite(fun); }
封装以后 /// <summary> /// 续期 /// </summary> public void postpone() { new redisclient<object>().ksetentryin(sessionid, new timespan(0, managers.timeout, 0)); }
这里我利用了mvc3中的actionfilter,拦截用户的所有请求
namespace test { public class sessionfilterattribute : actionfilterattribute { /// <summary> /// 每次请求都续期 /// </summary> /// <param name="filtercontext"></param> public override void onactionexecuting(actionexecutingcontext filtercontext) { new session(filtercontext.httpcontext).postpone(); } } }
在global.asax中要注册一下
public static void registerglobalfilters(globalfiltercollection filters) { filters.add(new sessionfilterattribute()); } protected void application_start() { registerglobalfilters(globalfilters.filters); }
5.调用方式
为了方便调用借用4.0中的新特性,把controller添加一个扩展属性
public static class extsessions {public static session sessionext(this controller controller) { return new session(controller.httpcontext); } }
调用方法
public class homecontroller : controller { public actionresult index() { this.sessionext().islogin(); return view(); } }
6.代码下载
7.后续
sessionmanager包含 获取用户列表数量,注销某个用户,根据用户id获取用户信息,在线用户对象列表,在线用户sessionid列表等方法
后续将实现用户一处登录功能
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: ASP.NET MVC如何使用Unity实现Ioc详解
下一篇: 大数据硕士专业:顺势而上的机遇
推荐阅读