分布式中使用 Redis 实现 Session 共享(下)
来源:互联网 发布:电脑淘宝淘口令怎么弄 编辑:程序博客网 时间:2024/05/19 06:37
http://blog.jobbole.com/91877/
上一篇使用Redis实现Session共享方式虽然可行,但是实际操作起来却很麻烦,现有代码已经是这个样子了,总不可能全部换掉吧!好吧,这是个很实际的问题,那么能不能实现无侵入式的分布式Session共享方案呢?mode=”InProc”这是web.config里面使用iis进程保存Session的配置,不知你注意过没,mode除了InProc,SQLServer,StateServer这几个常用的,还有一个Custom。这里我要使用的是网友提供给的一种方自定义Session,需要继承System.Web.SessionState.SessionStateStoreProviderBase实现自己的SessionStateStoreProvide,下面讲解如何实现自定义的RedisSessionStateStore。
SessionStateStoreProviderBase
SessionStateStoreProviderBase是asp.net提供的定义数据存储区的会话状态提供程序所需的成员。像常用InProcSessionStateStore(mode=”InProc”),SqlSessionStateStore(mode=”SQLServer”),都是继承SessionStateStoreProviderBase实现的存储。我们来看看msdn对其成员的定义
如果 regenerateExpiredSessionId 属性设置为 true,则 actionFlags 参数用于其 Cookieless 属性为 true 的会话。actionFlags 值设置为 InitializeItem (1) 则指示会话数据存储区中的项是需要初始化的新会话。通过调用CreateUninitializedItem 方法可以创建会话数据存储区中未初始化的项。如果会话数据存储区中的项已经初始化,则 actionFlags 参数设置为零。
如果提供程序支持无 Cookie 会话,请将 actionFlags 输出参数设置为当前项从会话数据存储区中返回的值。如果被请求的会话存储项的 actionFlags 参数值等于InitializeItem 枚举值 (1),则 GetItemExclusive 方法在设置 actionFlags out
参数之后应将数据存储区中的值设置为零。
会话数据存储区中未初始化的项与新生成的 SessionID值关联,并且仅包含默认值,其中包括到期日期和时间以及与 GetItem 和 GetItemExclusive 方法的actionFlags 参数相对应的值。会话状态存储区中的未初始化项应包含一个与 InitializeItem 枚举值 (1) 相等的actionFlags 值。此值由 GetItem 和GetItemExclusive 方法传递给SessionStateModule,并针对 SessionStateModule指定当前会话是新会话。然后,SessionStateModule将初始化该新会话,并引发 Session_OnStart 事件。
CreateNewStoreData 方法采用当前请求的 HttpContext 实例和当前会话的Timeout 值作为输入,并返回带有空ISessionStateItemCollection 对象的新的SessionStateStoreData 对象、一个HttpStaticObjectsCollection 集合和指定的 Timeout值。使用 GetSessionStaticObjects 方法可以检索 ASP.NET 应用程序的 HttpStaticObjectsCollection 实例。上面的定义有点长,其实很多都在说明一点那就是原生了Session是单线程的方式实现的,当多个进行读的时候会加锁后面的会等待。我们下面实现的去掉了这些锁,加快并发读写。
实现自己的RedisSessionStateStore
继承SessionStateStoreProviderBase实现自己的RedisSessionStateStore也很简单,只需继承SessionStateStoreProviderBase重写CreateNewStoreData,CreateUninitializedItem,GetItem等几个方法即可,下面贴出代码,参考InProcSessionStateStore实现。
/// <summary>
/// 使用Redis实现SessionStateStoreProviderBase
/// </summary>
public
class
RedisSessionStateStore : SessionStateStoreProviderBase
{
/// <summary>
/// 创建新的Session执行
/// </summary>
public
override
SessionStateStoreData CreateNewStoreData(HttpContext context,
int
timeout)
{
return
CreateLegitStoreData(context,
null
,
null
, timeout);
}
internal
static
SessionStateStoreData CreateLegitStoreData(HttpContext context, ISessionStateItemCollection sessionItems, HttpStaticObjectsCollection staticObjects,
int
timeout)
{
if
(sessionItems ==
null
)
sessionItems =
new
SessionStateItemCollection();
if
(staticObjects ==
null
&& context !=
null
)
staticObjects = SessionStateUtility.GetSessionStaticObjects(context);
return
new
SessionStateStoreData(sessionItems, staticObjects, timeout);
}
public
override
void
CreateUninitializedItem(HttpContext context,
string
id,
int
timeout)
{
RedisSessionState state =
new
RedisSessionState(
null
,
null
, timeout);
RedisBase.Item_Set<
string
>(id, state.ToJson(),timeout);
}
private
SessionStateStoreData DoGet(HttpContext context,
string
id,
bool
exclusive,
out
bool
locked,
out
TimeSpan lockAge,
out
object
lockId,
out
SessionStateActions actionFlags)
{
locked =
false
;
lockId =
null
;
lockAge = TimeSpan.Zero;
actionFlags = SessionStateActions.None;
RedisSessionState state = RedisSessionState.FromJson(RedisBase.Item_Get<
string
>(id));
if
(state ==
null
)
{
return
null
;
}
RedisBase.Item_SetExpire(id, state._timeout);
return
CreateLegitStoreData(context, state._sessionItems, state._staticObjects, state._timeout);
}
/// <summary>
/// 取值的时候执行
/// </summary>
public
override
SessionStateStoreData GetItem(HttpContext context,
string
id,
out
bool
locked,
out
TimeSpan lockAge,
out
object
lockId,
out
SessionStateActions actionFlags)
{
return
this
.DoGet(context, id,
false
,
out
locked,
out
lockAge,
out
lockId,
out
actionFlags);
}
public
override
SessionStateStoreData GetItemExclusive(HttpContext context,
string
id,
out
bool
locked,
out
TimeSpan lockAge,
out
object
lockId,
out
SessionStateActions actionFlags)
{
return
this
.DoGet(context, id,
true
,
out
locked,
out
lockAge,
out
lockId,
out
actionFlags);
}
/// <summary>
/// 新增 修改 移除键值时执行
/// </summary>
/// <param name="item">item.Items为当前所有的键值对</param>
public
override
void
SetAndReleaseItemExclusive(HttpContext context,
string
id, SessionStateStoreData item,
object
lockId,
bool
newItem)
{
ISessionStateItemCollection sessionItems =
null
;
HttpStaticObjectsCollection staticObjects =
null
;
if
(item.Items.Count > 0)
sessionItems = item.Items;
if
(!item.StaticObjects.NeverAccessed)
staticObjects = item.StaticObjects;
RedisSessionState state2 =
new
RedisSessionState(sessionItems, staticObjects, item.Timeout);
RedisBase.Item_Set<
string
>(id, state2.ToJson(), item.Timeout);
}
#region "未实现方法"
public
override
void
Dispose()
{
}
public
override
void
EndRequest(HttpContext context)
{
}
public
override
void
InitializeRequest(HttpContext context)
{
}
public
override
void
ReleaseItemExclusive(HttpContext context,
string
id,
object
lockId)
{
}
public
override
void
RemoveItem(HttpContext context,
string
id,
object
lockId, SessionStateStoreData item)
{
RedisBase.Item_Remove(id);
}
public
override
void
ResetItemTimeout(HttpContext context,
string
id)
{
}
public
override
bool
SetItemExpireCallback(SessionStateItemExpireCallback expireCallback)
{
return
true
;
}
#endregion
}
internal
sealed
class
SessionStateItem
{
public
Dictionary<
string
,
object
> Dict;
public
int
Timeout;
}
internal
sealed
class
RedisSessionState
{
internal
ISessionStateItemCollection _sessionItems;
internal
HttpStaticObjectsCollection _staticObjects;
internal
int
_timeout;
internal
RedisSessionState(ISessionStateItemCollection sessionItems, HttpStaticObjectsCollection staticObjects,
int
timeout)
{
this
.Copy(sessionItems, staticObjects, timeout);
}
internal
void
Copy(ISessionStateItemCollection sessionItems, HttpStaticObjectsCollection staticObjects,
int
timeout)
{
this
._sessionItems = sessionItems;
this
._staticObjects = staticObjects;
this
._timeout = timeout;
}
public
string
ToJson()
{
// 这里忽略_staticObjects这个成员。
if
(_sessionItems ==
null
|| _sessionItems.Count == 0)
{
return
null
;
}
Dictionary<
string
,
object
> dict =
new
Dictionary<
string
,
object
>(_sessionItems.Count);
string
key;
NameObjectCollectionBase.KeysCollection keys = _sessionItems.Keys;
for
(
int
i = 0; i < keys.Count; i++)
{
key = keys[i];
dict.Add(key, _sessionItems[key]);
}
SessionStateItem item =
new
SessionStateItem { Dict = dict, Timeout =
this
._timeout };
return
JsonConvert.SerializeObject(item);
}
public
static
RedisSessionState FromJson(
string
json)
{
if
(
string
.IsNullOrEmpty(json))
{
return
null
;
}
try
{
SessionStateItem item = JsonConvert.DeserializeObject<SessionStateItem>(json);
SessionStateItemCollection collections =
new
SessionStateItemCollection();
foreach
(KeyValuePair<
string
,
object
> kvp
in
item.Dict)
{
collections[kvp.Key] = kvp.Value;
}
return
new
RedisSessionState(collections,
null
, item.Timeout);
}
catch
{
return
null
;
}
}
}
使用也很简单,修改web.config,如下图
然后可以保持原先代码不变,像Session["UserCode"]=”admin”方式进行使用,但是现在的Session已经具备了分布式的特征,支持跨域。这里得说一下该方式的缺点,在GetItem和SetAndReleaseItemExclusive时需要对键值对进行反序列化和序列化操作,对于保存数据量大的情况反而性能相对于系统提供的方式大打折扣,所以使用的时候需要考虑自己的实际场景。
总结
本来分布式Session共享到上篇就完结了,但是由于方案的可行性差,还有更好的方案,所以花了点时间参考了前面MSND中的说明,和ASP.net源码中InProcSessionStateStore的实现,解决GetItemExclusive等方法的并发锁定问题,最终实现了Redis的存储方式,更加灵活方便。这里要感谢大家提的意见,让我又学会了一个知识点!
资源下载地址:redis_demo
svn下载地址:http://code.taobao.org/svn/ResidSessionDemo/
本文参考:
sessionstatestoreproviderbase定义:https://msdn.microsoft.com/zh-cn/library/system.web.sessionstate.sessionstatestoreproviderbase(VS.80).aspx
sessionstatestoreproviderbase成员:https://msdn.microsoft.com/zh-cn/library/ms178587(v=vs.80).aspx
相关文章
可能感兴趣的话题
- Python用途广泛,但是大学却很少开这门课程... · 24
- 各位程序员,大家平时除了code,工作之余还有什么兴... · 6
- Android保存修改数据问题 · 3
- 一道ACM校赛题-大家来帮我看看啊
- 学好Java反射,你就可以轻松搞定Java框架。
- 百度之星大赛——一道简单而不简约的算法题
- 局部变量和成员变量命名冲突问题 · 6
- 搞 IT 的怎么拿到年薪27万 · 2
- 大家有没有了解前端绘制拓扑图的开源库或插件? · 2
- Python有哪些库带给你最美的编程体验? · 6
- 分布式中使用 Redis 实现 Session 共享(下)
- 分布式中使用Redis实现Session共享
- 分布式中使用Redis实现Session共享
- 分布式中使用 Redis 实现 Session 共享(中)
- 分布式中使用 Redis 实现 Session 共享(上)
- 分布式中使用Redis实现Session共享(一)
- 分布式中使用Redis实现Session共享(二)
- 分布式中使用Redis实现Session共享(一)
- redis实现分布式session共享
- redis实现分布式session共享
- Spring Session + Redis实现分布式Session共享
- Spring Session + Redis实现分布式Session共享
- Spring Session + Redis实现分布式Session共享
- Spring Session + Redis实现分布式Session共享
- Spring Session + Redis实现分布式Session共享
- Spring Session + Redis实现分布式Session共享
- Spring Session + Redis实现分布式Session共享
- Spring Session + Redis实现分布式Session共享
- 如何安装sonic-visualiser提取音高
- RMySQL
- 保护手机号码不被应用窃取的有力措施
- MySQL去除列行首空格的方法
- 查询Oracle表结构和注释信息
- 分布式中使用 Redis 实现 Session 共享(下)
- cuda shareMemory bank conflict 探究
- 利用Echarts来制作图表
- Intellij Idea中运行tomcat 报內存溢出 解决方案
- Errors running builder 'DeploymentBuilder' on project
- 深度学习之卷积神经网络
- jieba(结巴)Python分词器加载到Eclipse方法
- Install Ruby on rails on Ubuntu 14.04 LST
- 安卓APK反编译与混淆编译