Redis中的StackExchange.redis在C#中的应用

Redis,基于这种Key-Value的内存数据库很适合来做分布式Session。本示例将基于StackExchange.Redis.dll进行实现,序列化使用的是Newtonsoft.json.dll。

为了避免APP等客户端重复登录导致同一个用户出现重复的Session,将以ServerID(可以是数据库主键)为服务端令牌,并生成一个新的GUID作为其客户端令牌,客户端令牌将返回给客户端。
两个令牌都将存储在Redis中,形成2个键值对,在服务端令牌中保存了其对应的客户端令牌ID。当同一用户再次登录时,将首先检查是否有基于其ServerID的服务端令牌存在,如果存在,则直接取其值(客户端令牌)返回给客户端,以此避免重复的Session浪费。

Redis中的StackExchange.redis在C#中的应用


Web.config:

[csharp] view plain copy
 print?
  1. <!--Redis数据库编号(范围:0~15)-->  
  2. <add key="SessionRedisDBIndex" value="0"/>  
  3. <!--主Redis连接串-->  
  4. <add key="SessionRedisMaster" value="pass:192.168.1.236:6379"/>  
  5. <!--Session有效时长,单位:分钟-->  
  6. <add key="SessionTimeOut" value="120"/>  

Session基类:

[csharp] view plain copy
 print?
  1. public abstract class BaseSession  
  2.     {  
  3.         DateTime mExpiresTime = DateTime.Now;  
  4.   
  5.         [JsonConverter(typeof(TimestampConverter))]  
  6.         public DateTime ExpiresTime  
  7.         {  
  8.             get { return mExpiresTime; }  
  9.         }  
  10.   
  11.         /// <summary>  
  12.         /// 设置Session过期时间  
  13.         /// </summary>  
  14.         /// <param name="aExpiresTime"></param>  
  15.         internal void SetExpiresTime(DateTime aExpiresTime)  
  16.         {  
  17.             mExpiresTime = aExpiresTime;  
  18.         }  
  19.   
  20.         [JsonIgnore]  
  21.         /// <summary>  
  22.         /// 获取服务端ID  
  23.         /// </summary>  
  24.         public abstract string ServerID { get; }  
  25.     }  
[csharp] view plain copy
 print?
  1. public static class SessionService  
  2.    {  
  3.        /// <summary>  
  4.        /// Session有效时长,默认120分钟  
  5.        /// </summary>  
  6.        static int TimeOutMinutes=120;  
  7.   
  8.        /// <summary>  
  9.        /// Redis缓存访问对象  
  10.        /// </summary>  
  11.        static ConnectionMultiplexer mRedisSession;  
  12.   
  13.        /// <summary>  
  14.        /// Session管理  
  15.        /// </summary>  
  16.        static SessionService()  
  17.        {  
  18.            try  
  19.            {  
  20.                TimeOutMinutes = int.Parse(ConfigurationManager.AppSettings["SessionTimeOut"]);  
  21.                string[] redisMaster = ConfigurationManager.AppSettings["SessionRedisMaster"].Split(':');  
  22.                ConfigurationOptions fRedisConfig = new ConfigurationOptions()  
  23.                {  
  24.                    Password = redisMaster[0],  
  25.                    EndPoints ={ { redisMaster[1], int.Parse(redisMaster[2]) } },  
  26.                    KeepAlive = 180,  
  27.                    DefaultDatabase = int.Parse(ConfigurationManager.AppSettings["SessionRedisDBIndex"])  
  28.                };  
  29.                mRedisSession = ConnectionMultiplexer.Connect(fRedisConfig);  
  30.            }  
  31.            catch (Exception ex)  
  32.            {  
  33.                Lib.LocalLog.Append("Session Error(SessionService):" + ex.Message, """SessionError""SessionError");  
  34.                throw ex;  
  35.            }  
  36.        }  
  37.   
  38.        /// <summary>  
  39.        /// 添加Session  
  40.        /// </summary>  
  41.        /// <typeparam name="T">Session对象类型</typeparam>  
  42.        /// <param name="aSession">要存为Session的对象</param>  
  43.        /// <param name="aPrefix">前缀</param>  
  44.        /// <returns>令牌</returns>  
  45.        public static string Add<T>(T aSession,string aPrefix="") where T:BaseSession  
  46.        {  
  47.            try  
  48.            {  
  49.                IDatabase client = mRedisSession.GetDatabase();  
  50.                aSession.SetExpiresTime(DateTime.Now.AddMinutes(TimeOutMinutes));  
  51.                string fExistToken = client.StringGet(aSession.ServerID);//获取服务端唯一码当前对应的客户端令牌  
  52.                if (fExistToken != null)  
  53.                {//存在客户端令牌  
  54.                    //更新客户端令牌  
  55.                    if (client.StringSet(fExistToken,  
  56.                        JsonConvert.SerializeObject(aSession),   
  57.                        aSession.ExpiresTime.Subtract(DateTime.Now)))  
  58.                    {  
  59.                        return fExistToken;  
  60.                    }  
  61.                }  
  62.                else  
  63.                {  
  64.                    string token = aPrefix + Guid.NewGuid().ToString("N");  
  65.                    if(client.StringSet(token,  
  66.                        JsonConvert.SerializeObject(aSession),  
  67.                        aSession.ExpiresTime.Subtract(DateTime.Now)) &&//添加Session  
  68.                       client.StringSet(aSession.ServerID,  
  69.                        token,  
  70.                        aSession.ExpiresTime.AddSeconds(-5).Subtract(DateTime.Now)))//绑定服务端唯一码与客户端令牌(将比Session早5秒失效)  
  71.                    {  
  72.                        return token;  
  73.                    }  
  74.                }  
  75.            }  
  76.            catch (Exception ex)  
  77.            {  
  78.                Lib.LocalLog.Append("Session Error(Add<T>):" + ex.Message, """SessionError""SessionError");  
  79.            }  
  80.            return null;  
  81.        }  
  82.   
  83.        /// <summary>  
  84.        /// 获取Session  
  85.        /// </summary>  
  86.        /// <typeparam name="T">Session对象类型</typeparam>  
  87.        /// <param name="aToken">令牌</param>  
  88.        /// <returns>令牌对应的Session对象</returns>  
  89.        public static T Get<T>(string aToken) where T : BaseSession  
  90.        {  
  91.            try  
  92.            {  
  93.                IDatabase client = mRedisSession.GetDatabase();  
  94.                T fEntity = null;  
  95.                string fSessionValue = client.StringGet(aToken);  
  96.                if (!string.IsNullOrEmpty(fSessionValue))  
  97.                {  
  98.                    fEntity = JsonConvert.DeserializeObject<T>(fSessionValue);  
  99.                    CheckExpireTime<T>(aToken, fEntity, client);  
  100.                }  
  101.                return fEntity;  
  102.            }  
  103.            catch (Exception ex)  
  104.            {  
  105.                Lib.LocalLog.Append("Session Error(Get<T>):" + ex.Message, """SessionError""SessionError");  
  106.            }  
  107.            return default(T);  
  108.        }  
  109.   
  110.        /// <summary>  
  111.        /// 获取Session 字符串值  
  112.        /// </summary>  
  113.        /// <param name="aToken">令牌</param>  
  114.        /// <returns>令牌对应的Session 字符串值</returns>  
  115.        public static string GetValue<T>(string aToken) where T : BaseSession  
  116.        {  
  117.            try  
  118.            {  
  119.                IDatabase client = mRedisSession.GetDatabase();  
  120.                string fSessionValue = client.StringGet(aToken);  
  121.                if (fSessionValue != null)  
  122.                {  
  123.                    CheckExpireTime<T>(aToken, JsonConvert.DeserializeObject<T>(fSessionValue), client);  
  124.                }  
  125.                return fSessionValue;  
  126.            }  
  127.            catch (Exception ex)  
  128.            {  
  129.                Lib.LocalLog.Append("Session Error(GetValue<T>):" + ex.Message, """SessionError""SessionError");  
  130.            }  
  131.            return null;  
  132.        }  
  133.   
  134.        /// <summary>  
  135.        /// 删除Session  
  136.        /// </summary>  
  137.        /// <param name="aToken">令牌</param>  
  138.        public static void Remove<T>(string aToken) where T : BaseSession  
  139.        {  
  140.            try  
  141.            {  
  142.                IDatabase client = mRedisSession.GetDatabase();  
  143.                T fEntity = null;  
  144.                string fSessionValue = client.StringGet(aToken);  
  145.                if (!string.IsNullOrEmpty(fSessionValue))  
  146.                {  
  147.                    fEntity = JsonConvert.DeserializeObject<T>(fSessionValue);  
  148.                    client.KeyDelete(fEntity.ServerID);  
  149.                    client.KeyDelete(aToken);  
  150.                }  
  151.            }  
  152.            catch (Exception ex)  
  153.            {  
  154.                Lib.LocalLog.Append("Session Error(Remove):" + ex.Message, """SessionError""SessionError");  
  155.            }  
  156.        }  
  157.   
  158.        /// <summary>  
  159.        /// 令牌有效时间检查  
  160.        /// </summary>  
  161.        /// <typeparam name="T"></typeparam>  
  162.        /// <param name="aToken">令牌</param>  
  163.        /// <param name="aSession"></param>  
  164.        static void CheckExpireTime<T>(string aToken, T aSession, IDatabase client) where T : BaseSession  
  165.        {  
  166.            if (aSession.ExpiresTime.Subtract(DateTime.Now).TotalMinutes < 10)  
  167.            {//离Session过期时间小于10分钟,延长Session有效期  
  168.                try  
  169.                {  
  170.                    aSession.SetExpiresTime(DateTime.Now.AddMinutes(TimeOutMinutes));  
  171.                    client.StringSet(aToken, JsonConvert.SerializeObject(aSession), aSession.ExpiresTime.Subtract(DateTime.Now));  
  172.                    client.KeyExpire(aSession.ServerID, aSession.ExpiresTime.AddSeconds(-5).Subtract(DateTime.Now));  
  173.                }  
  174.                catch (Exception ex)  
  175.                {  
  176.                    Lib.LocalLog.Append("Session Error(6):" + ex.Message, """SessionError""SessionError");  
  177.                }  
  178.            }  
  179.        }  
  180.    }