分布式缓存扩展Session机制
为什么要把Session放在缓存中
Session是我们常用的状态保持的对象,它通常会生成一个唯一的SessionId以Cookie的方式存在浏览器端,而Session本身会保存在服务器端。
虽说我们用Session很方便的实现状态保持,但是Session也带来了很多弊端,下面我们一步一步来分析Session的一些弊端以及用什么方式去改变它:
一、当mode="InProc"时:
1.因为网站会因为各种原因重启,照成数据丢失,在线用户全部下线。
2.Session保存的东西越来越多,占用服务器内存也就越来越大,服务器内存压力也会越来越大。
3.Session存在服务器内存中,不能在多台服务器之间共享,扩展性收到了影响。
-----解决方案:model="SQLServer"或者model="StateServer"...
二、当model="SQLServer"或者model="StateServer"时:
1.无论我们是否要使用SessionState,SessionStateModule一直在工作,每一个用户请求都会去处理SessionState,执行一些列的调用,其中还可能涉及到序列化和反序列化,这样造成了资源浪费,影响性能。
2.每个用户对应一个SessionId,当一个用户同时打开两个页面的时候、或者一个页面中夹杂着IFrame、或者带着异步请求,这样算属于单个用户并发吧?!HttpHandler处理的时候是会对Session进行加锁的,当一个页面处理慢的时候会锁住当前的Session,后面的页面是不是也在等待当前Session的释放,就这样就照成一个问题——Session影响并发(单个用户并发)。虽说这种情况很少会出现,但是我们在处理一些特殊请求的时候,确实需要注意一下这个细节。对于上面所说的相信大家都有所理解,Session影响并发,可能没有太过关注过,下面举个例子试一试是不是这样的情况!
先创建一个主窗体,三个父窗体,我注册了HttpModule来看一下执行的流程,Test_2页面线程睡眠5秒...
在未使用Session的情况下...
然后我在主窗体后台类中使用了Session Session["test"]="test"; 会发现Test_3被阻塞了...
解决方案:使用分布式缓存,最近有很多比较火热的分布式缓存系统,如:Memcached、Redis...
因为Redis可以实现数据持久化,支持多重数据类型,可能应用场景更广泛一些,所以之前用了Redis,这次就那Redis来做例子,不过其他的实现方式也一样一样了...
------------------------------------------------------------------------------------------------------------------------------------------------------
首先分析一下我们怎么来设计这个Session,我的想法是每个SessionId对应一个Dictionary<string,Object>,SessionId用Guid表示,也是以Cookie的形式发送在客户端,Session为键,Dictionary为值存放在Redis里...
Session——SessionId{Dictionary<string,Object>}
SessionId{Dictionary<string,Object>}
SessionId{Dictionary<string,Object>}
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Web;
//使用Redis的命名空间 namespace ServiceStack.Redis {
//把Session作为Redis的扩展方法: 三要素——静态类、静态方法、this关键字 public static class RedisHelper { //private static RedisClient Redis = new RedisClient("127.0.0.1", 6379);
//设置Session public static bool SetSession(this RedisClient Redis, string key, object value) { var requestCookie = HttpContext.Current.Request.Cookies["SessionId"]; string sessionId; if (requestCookie == null) { sessionId = Guid.NewGuid().ToString(); } else { sessionId = requestCookie.Value; } Redis.Expire(sessionId, 20 * 60); HttpContext.Current.Response.SetCookie(new HttpCookie("SessionId", sessionId)); if (Redis.Exists(sessionId) == 1 ? true : false) { var newDir = Redis.Get<Dictionary<string, object>>(sessionId) as Dictionary<string, object>; if (newDir == null) { return false; } if (newDir.ContainsKey(key)) { newDir.Remove(key); } newDir[key] = value; Redis.Set<Dictionary<string, object>>(sessionId, newDir); return true; } else { Dictionary<string, object> dic = new Dictionary<string, object>(); dic.Add(key, value); Redis.Set<Dictionary<string, object>>(sessionId, dic); return true; } }
//删除Session
public static bool DeleteSession(this RedisClient Redis, string key) { var requestCookie = HttpContext.Current.Request.Cookies["SessionId"]; string sessionId; if (requestCookie == null) { return false; } sessionId = requestCookie.Value; Redis.Expire(sessionId, 20 * 60); Dictionary<string, object> dic = Redis.Get<Dictionary<string, object>>(sessionId); if (dic == null) { return false; } if (dic.ContainsKey(key)) { dic.Remove(key); Redis.Set<Dictionary<string, object>>(sessionId, dic); } return true; }
//获取Session public static object GetSession(this RedisClient Redis, string key) { var requestCookie = HttpContext.Current.Request.Cookies["SessionId"]; string sessionId; if (requestCookie == null) { return null; } sessionId = requestCookie.Value; Dictionary<string, object> dic = Redis.Get<Dictionary<string, object>>(sessionId); if (dic == null) { return null; } if (dic.ContainsKey(key)) { Redis.Expire(sessionId, 20 * 60); return dic[key]; } return null; } } }
优点:
1.解决了session的性能问题;
2.解决了应用程序的可扩展性;
3.内存之间的共享;
4.Session丢失的问题;
这是自己对Redis的一个扩展Session,不知道是否算优,欢迎大牛拍砖纠正;