您的位置:首页 > 数据库 > Redis

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

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

为了避免APP等客户端重复登录导致同一个用户出现重复的Session,将以ServerID(可以是数据库主键)为服务端令牌,并生成一个新的GUID作为其客户端令牌,客户端令牌将返回给客户端。

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



Web.config:

<!--Redis数据库编号(范围:0~15)-->
<add key="SessionRedisDBIndex" value="0"/>
<!--主Redis连接串-->
<add key="SessionRedisMaster" value="pass:192.168.1.236:6379"/>
<!--Session有效时长,单位:分钟-->
<add key="SessionTimeOut" value="120"/>


Session基类:

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

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

/// <summary>
/// 设置Session过期时间
/// </summary>
/// <param name="aExpiresTime"></param>
internal void SetExpiresTime(DateTime aExpiresTime)
{
mExpiresTime = aExpiresTime;
}

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

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

/// <summary>
/// Session管理
/// </summary>
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;
}
}

/// <summary>
/// 添加Session
/// </summary>
/// <typeparam name="T">Session对象类型</typeparam>
/// <param name="aSession">要存为Session的对象</param>
/// <param name="aPrefix">前缀</param>
/// <returns>令牌</returns>
public static string Add<T>(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<T>):" + ex.Message, "", "SessionError", "SessionError");
}
return null;
}

/// <summary>
/// 获取Session
/// </summary>
/// <typeparam name="T">Session对象类型</typeparam>
/// <param name="aToken">令牌</param>
/// <returns>令牌对应的Session对象</returns>
public static T Get<T>(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<T>(fSessionValue);
CheckExpireTime<T>(aToken, fEntity, client);
}
return fEntity;
}
catch (Exception ex)
{
Lib.LocalLog.Append("Session Error(Get<T>):" + ex.Message, "", "SessionError", "SessionError");
}
return default(T);
}

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

/// <summary>
/// 删除Session
/// </summary>
/// <param name="aToken">令牌</param>
public static void Remove<T>(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<T>(fSessionValue);
client.KeyDelete(fEntity.ServerID);
client.KeyDelete(aToken);
}
}
catch (Exception ex)
{
Lib.LocalLog.Append("Session Error(Remove):" + ex.Message, "", "SessionError", "SessionError");
}
}

/// <summary>
/// 令牌有效时间检查
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="aToken">令牌</param>
/// <param name="aSession"></param>
static void CheckExpireTime<T>(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");
}
}
}
}



                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  .net redis session 分布式 c#