C# 基于StackExchange.Redis.dll利用Redis实现分布式Session

最近在研发一款O2O产品,考虑到分布式架构的需要,以前那一套.NET的Session管理方式已经不合用了。遂研究了一下Redis,发现基于这种Key-Value的内存数据库很适合来做分布式Session。本示例将基于StackExchange.Redis.dll进行实现,序列化使用的是Newtonsoft.json.dll。

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

C# 基于StackExchange.Redis.dll利用Redis实现分布式Session_第1张图片


Web.config:






Session基类:

public abstract class BaseSession
    {
        DateTime mExpiresTime = DateTime.Now;

        [JsonConverter(typeof(TimestampConverter))]
        public DateTime ExpiresTime
        {
            get { return mExpiresTime; }
        }

        /// 
        /// 设置Session过期时间
        /// 
        /// 
        internal void SetExpiresTime(DateTime aExpiresTime)
        {
            mExpiresTime = aExpiresTime;
        }

        [JsonIgnore]
        /// 
        /// 获取服务端ID
        /// 
        public abstract string ServerID { get; }
    }
 public static class SessionService
    {
        /// 
        /// Session有效时长,默认120分钟
        /// 
        static int TimeOutMinutes=120;

        /// 
        /// Redis缓存访问对象
        /// 
        static ConnectionMultiplexer mRedisSession;

        /// 
        /// Session管理
        /// 
        static SessionService()
        {
            try
            {
                TimeOutMinutes = int.Parse(ConfigurationManager.AppSettings["SessionTimeOut"]);
                string[] redisMaster = ConfigurationManager.AppSettings["SessionRedisMaster"].Split(':');
                ConfigurationOptions fRedisConfig = new ConfigurationOptions()
                {
                    Password = redisMaster[0],
                    EndPoints ={ { redisMaster[1], int.Parse(redisMaster[2]) } },
                    KeepAlive = 180,
                    DefaultDatabase = int.Parse(ConfigurationManager.AppSettings["SessionRedisDBIndex"])
                };
                mRedisSession = ConnectionMultiplexer.Connect(fRedisConfig);
            }
            catch (Exception ex)
            {
                Lib.LocalLog.Append("Session Error(SessionService):" + ex.Message, "", "SessionError", "SessionError");
                throw ex;
            }
        }

        /// 
        /// 添加Session
        /// 
        /// Session对象类型
        /// 要存为Session的对象
        /// 前缀
        /// 令牌
        public static string Add(T aSession,string aPrefix="") where T:BaseSession
        {
            try
            {
                IDatabase client = mRedisSession.GetDatabase();
                aSession.SetExpiresTime(DateTime.Now.AddMinutes(TimeOutMinutes));
                string fExistToken = client.StringGet(aSession.ServerID);//获取服务端唯一码当前对应的客户端令牌
                if (fExistToken != null)
                {//存在客户端令牌
                    //更新客户端令牌
                    if (client.StringSet(fExistToken,
                        JsonConvert.SerializeObject(aSession), 
                        aSession.ExpiresTime.Subtract(DateTime.Now)))
                    {
                        return fExistToken;
                    }
                }
                else
                {
                    string token = aPrefix + Guid.NewGuid().ToString("N");
                    if(client.StringSet(token,
                        JsonConvert.SerializeObject(aSession),
                        aSession.ExpiresTime.Subtract(DateTime.Now)) &&//添加Session
                       client.StringSet(aSession.ServerID,
                        token,
                        aSession.ExpiresTime.AddSeconds(-5).Subtract(DateTime.Now)))//绑定服务端唯一码与客户端令牌(将比Session早5秒失效)
                    {
                        return token;
                    }
                }
            }
            catch (Exception ex)
            {
                Lib.LocalLog.Append("Session Error(Add):" + ex.Message, "", "SessionError", "SessionError");
            }
            return null;
        }

        /// 
        /// 获取Session
        /// 
        /// Session对象类型
        /// 令牌
        /// 令牌对应的Session对象
        public static T Get(string aToken) where T : BaseSession
        {
            try
            {
                IDatabase client = mRedisSession.GetDatabase();
                T fEntity = null;
                string fSessionValue = client.StringGet(aToken);
                if (!string.IsNullOrEmpty(fSessionValue))
                {
                    fEntity = JsonConvert.DeserializeObject(fSessionValue);
                    CheckExpireTime(aToken, fEntity, client);
                }
                return fEntity;
            }
            catch (Exception ex)
            {
                Lib.LocalLog.Append("Session Error(Get):" + ex.Message, "", "SessionError", "SessionError");
            }
            return default(T);
        }

        /// 
        /// 获取Session 字符串值
        /// 
        /// 令牌
        /// 令牌对应的Session 字符串值
        public static string GetValue(string aToken) where T : BaseSession
        {
            try
            {
                IDatabase client = mRedisSession.GetDatabase();
                string fSessionValue = client.StringGet(aToken);
                if (fSessionValue != null)
                {
                    CheckExpireTime(aToken, JsonConvert.DeserializeObject(fSessionValue), client);
                }
                return fSessionValue;
            }
            catch (Exception ex)
            {
                Lib.LocalLog.Append("Session Error(GetValue):" + ex.Message, "", "SessionError", "SessionError");
            }
            return null;
        }

        /// 
        /// 删除Session
        /// 
        /// 令牌
        public static void Remove(string aToken) where T : BaseSession
        {
            try
            {
                IDatabase client = mRedisSession.GetDatabase();
                T fEntity = null;
                string fSessionValue = client.StringGet(aToken);
                if (!string.IsNullOrEmpty(fSessionValue))
                {
                    fEntity = JsonConvert.DeserializeObject(fSessionValue);
                    client.KeyDelete(fEntity.ServerID);
                    client.KeyDelete(aToken);
                }
            }
            catch (Exception ex)
            {
                Lib.LocalLog.Append("Session Error(Remove):" + ex.Message, "", "SessionError", "SessionError");
            }
        }

        /// 
        /// 令牌有效时间检查
        /// 
        /// 
        /// 令牌
        /// 
        static void CheckExpireTime(string aToken, T aSession, IDatabase client) where T : BaseSession
        {
            if (aSession.ExpiresTime.Subtract(DateTime.Now).TotalMinutes < 10)
            {//离Session过期时间小于10分钟,延长Session有效期
                try
                {
                    aSession.SetExpiresTime(DateTime.Now.AddMinutes(TimeOutMinutes));
                    client.StringSet(aToken, JsonConvert.SerializeObject(aSession), aSession.ExpiresTime.Subtract(DateTime.Now));
                    client.KeyExpire(aSession.ServerID, aSession.ExpiresTime.AddSeconds(-5).Subtract(DateTime.Now));
                }
                catch (Exception ex)
                {
                    Lib.LocalLog.Append("Session Error(6):" + ex.Message, "", "SessionError", "SessionError");
                }
            }
        }
    }

 
 

你可能感兴趣的:(C#.NET,Redis,数据库)