现在很多项目都用Redis(RedisSessionStateProvider)来保存Session数据,但是最近遇到一个比较典型的情况,需要把用户数据全部load到redis里面,在加上RedisSessionStateProvider本身的数据,造成很多内存的浪费。首先我们要知道Session会针对用户的数据,比如用户在chrome登录会保存一套信息,用ie登录有保存一套信息,用FF登录还是保存一套信息,那么redis就保存了3套一样的session data数据。
如RedisSessionStateProvider默认会生成*****_Data来保存我们的session数据,******_Internal来记录session过期,这里的*****_2qfxy3ie51m5oneffzr0jkrd****和****_isudg4h01axx3lnd3pao4zcj****其实是用一个用户在2个浏览器上的数据,我们是否可以只保留一份****——Data数据,多分_Internal数据(用与表示到***_Data数据的关联)。
demo如下:
public class BaseController : Controller { string cookieName; public BaseController() { cookieName = SessionStateManage.CookieName; HttpCookie cookie = System.Web.HttpContext.Current.Request.Cookies[cookieName]; if (cookie != null) { SessionCookieValue = cookie.Value; if (string.IsNullOrEmpty(SessionCookieValue)) { UserSession = SessionStateManage.GetUser(SessionCookieValue); } } } public void Login(UserInfo info) { string sessionID = Guid.NewGuid().ToString(); System.Web.HttpContext.Current.Response.Cookies.Set(new HttpCookie(cookieName, sessionID)); SessionStateManage.SetLogin(sessionID, info); } public string SessionCookieValue { set; get; } public UserInfo UserSession { set; get; } }
public class HomeController : BaseController { public ActionResult Index() { if (UserSession == null) { UserSession = new UserInfo { Email = "gavin@test", Password = "123", UserID = "1", UserName = "gavin" }; Login(UserSession); } return View(); } public ActionResult About() { ViewBag.Message = "Your application description page."; return View(); } public ActionResult Contact() { ViewBag.Message = "Your contact page."; return View(); }
从当前请求中获取cookie,如果有就加载用户信息,如果没有我们在用户登录的时候除了写Redis还需要给浏览器写上cookie。
public class UserInfo { public string UserID { set; get; } public string UserName { set; get; } public string Password { set; get; } public string Email { set; get; } } public class SessionStateManage { static RedisHelper Redis; static SessionStateManage() { CookieName = ConfigurationManager.AppSettings["cookieName"] ?? "SESSION_ID"; ApplicationName = ConfigurationManager.AppSettings["ApplicationName"] ?? "SESSION"; Redis = new RedisHelper(1); Redis.SetSysCustomKey(ApplicationName); string sessionTimeOutStr = ConfigurationManager.AppSettings["SessionTimeOut"] ?? "20"; SessionTimeOut = int.Parse(sessionTimeOutStr); } #region Property public static string CookieName { get; private set; } public static string ApplicationName { get; private set; } public static int SessionTimeOut { get; private set; } #endregion public static void SetLogin(string cookieName, UserInfo user) { Redis.StringSet(cookieName, user.UserID); Redis.KeyExpire(cookieName, new TimeSpan(0, SessionTimeOut, 0)); Redis.HashSet(user.UserID, user); } public static UserInfo GetUser(string cookinNme) { string userID = Redis.StringGet(cookinNme); return userID == null ? null : Redis.HashGet<UserInfo>(userID); } }
用户的session过期直接用redis里面key的过期时间。redis操作需要辅助类如下:
/// <summary> /// ConnectionMultiplexer对象管理帮助类 /// </summary> public static class RedisConnectionHelp { //系统自定义Key前缀 public static readonly string SysCustomKey = ConfigurationManager.AppSettings["redisKey"] ?? ""; //"127.0.0.1:6379,allowadmin=true private static readonly string RedisConnectionString = ConfigurationManager.ConnectionStrings["RedisExchangeHosts"].ConnectionString; private static readonly object Locker = new object(); private static ConnectionMultiplexer _instance; private static readonly ConcurrentDictionary<string, ConnectionMultiplexer> ConnectionCache = new ConcurrentDictionary<string, ConnectionMultiplexer>(); /// <summary> /// 单例获取 /// </summary> public static ConnectionMultiplexer Instance { get { if (_instance == null) { lock (Locker) { if (_instance == null || !_instance.IsConnected) { _instance = GetManager(); } } } return _instance; } } /// <summary> /// 缓存获取 /// </summary> /// <param name="connectionString"></param> /// <returns></returns> public static ConnectionMultiplexer GetConnectionMultiplexer(string connectionString) { if (!ConnectionCache.ContainsKey(connectionString)) { ConnectionCache[connectionString] = GetManager(connectionString); } return ConnectionCache[connectionString]; } private static ConnectionMultiplexer GetManager(string connectionString = null) { connectionString = connectionString ?? RedisConnectionString; var connect = ConnectionMultiplexer.Connect(connectionString); //注册如下事件 connect.ConnectionFailed += MuxerConnectionFailed; connect.ConnectionRestored += MuxerConnectionRestored; connect.ErrorMessage += MuxerErrorMessage; connect.ConfigurationChanged += MuxerConfigurationChanged; connect.HashSlotMoved += MuxerHashSlotMoved; connect.InternalError += MuxerInternalError; return connect; } #region 事件 /// <summary> /// 配置更改时 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private static void MuxerConfigurationChanged(object sender, EndPointEventArgs e) { Console.WriteLine("Configuration changed: " + e.EndPoint); } /// <summary> /// 发生错误时 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private static void MuxerErrorMessage(object sender, RedisErrorEventArgs e) { Console.WriteLine("ErrorMessage: " + e.Message); } /// <summary> /// 重新建立连接之前的错误 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private static void MuxerConnectionRestored(object sender, ConnectionFailedEventArgs e) { Console.WriteLine("ConnectionRestored: " + e.EndPoint); } /// <summary> /// 连接失败 , 如果重新连接成功你将不会收到这个通知 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private static void MuxerConnectionFailed(object sender, ConnectionFailedEventArgs e) { Console.WriteLine("重新连接:Endpoint failed: " + e.EndPoint + ", " + e.FailureType + (e.Exception == null ? "" : (", " + e.Exception.Message))); } /// <summary> /// 更改集群 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private static void MuxerHashSlotMoved(object sender, HashSlotMovedEventArgs e) { Console.WriteLine("HashSlotMoved:NewEndPoint" + e.NewEndPoint + ", OldEndPoint" + e.OldEndPoint); } /// <summary> /// redis类库错误 /// </summary> /// <param name="sender"></par