基于MongoDB打造.Net的分布式Session子系统
基于MongoDB打造.Net的分布式Session子系统
Taobao有她自己的分布式session框架,.net阵营也不能落后了,在下做了个基于MongoDB的支持最多26台MongoDB的分布式Session框架。
先看看配置文件:

<?xml version="1.0" encoding="utf-8" ?>
<MongoDBSession>
<DbName>SessionDB</DbName>
<IdentityMap Identity="A">mongodb://localhost</IdentityMap>
<IdentityMap Identity="B">mongodb://localhost</IdentityMap>
<IdentityMap Identity="C">mongodb://localhost</IdentityMap>
<IdentityMap Identity="D">mongodb://localhost</IdentityMap>
<IdentityMap Identity="E">mongodb://localhost</IdentityMap>
<IdentityMap Identity="F">mongodb://localhost</IdentityMap>
<IdentityMap Identity="G">mongodb://localhost</IdentityMap>
<IdentityMap Identity="H">mongodb://localhost</IdentityMap>
<IdentityMap Identity="I">mongodb://localhost</IdentityMap>
<IdentityMap Identity="J">mongodb://localhost</IdentityMap>
<IdentityMap Identity="K">mongodb://localhost</IdentityMap>
<IdentityMap Identity="L">mongodb://localhost</IdentityMap>
<IdentityMap Identity="M">mongodb://localhost</IdentityMap>
<IdentityMap Identity="N">mongodb://localhost</IdentityMap>
<IdentityMap Identity="O">mongodb://localhost</IdentityMap>
<IdentityMap Identity="P">mongodb://localhost</IdentityMap>
<IdentityMap Identity="Q">mongodb://localhost</IdentityMap>
<IdentityMap Identity="R">mongodb://localhost</IdentityMap>
<IdentityMap Identity="S">mongodb://localhost</IdentityMap>
<IdentityMap Identity="T">mongodb://localhost</IdentityMap>
<IdentityMap Identity="U">mongodb://localhost</IdentityMap>
<IdentityMap Identity="V">mongodb://localhost</IdentityMap>
<IdentityMap Identity="W">mongodb://localhost</IdentityMap>
<IdentityMap Identity="X">mongodb://localhost</IdentityMap>
<IdentityMap Identity="Y">mongodb://localhost</IdentityMap>
<IdentityMap Identity="Z">mongodb://localhost</IdentityMap>
</MongoDBSession>

从Identity A一直到Z,默认分成了26个Map,具体的C#应用代码:

protected void btnTest_Click(object sender, EventArgs e)
{
Session["A"] = DateTime.Now;
Session["B"] = 1111111111111;
Session["C"] = "fffffffffffffff";
} protected void btnGetSession_Click(object sender, EventArgs e)
{
Response.Write(Session["A"].ToString());
Response.Write("<br />");
Response.Write(Session["B"].ToString());
Response.Write("<br />");
Response.Write(Session["C"].ToString());
}
protected void btnAbandon_Click(object sender, EventArgs e)
{
Session.Abandon();
}

呵呵,就是普通的Session用法。
这个要配置web.config:

<system.web>
<sessionState mode="Custom" customProvider="A2DSessionProvider" sessionIDManagerType="A2DFramework.SessionService.MongoDBSessionIDManager">
<providers>
<add name="A2DSessionProvider" type="A2DFramework.SessionService.MongoDBSessionStateStore"/>
</providers>
</sessionState>
</system.web>

这里会牵扯出2个类:
- A2DFramework.SessionService.MongoDBSessionIDManager
- A2DFramework.SessionService.MongoDBSessionStateStore
MongoDBSessionIDManager
- 自定义生成的cookie值(也就是SessionID),在这个sample中,会生成如“E.asadfalkasdfjal”这样的SessionID,其中前缀E代表这个Session的信息会映射到哪台MongoDB上。
- 关键代码
public class MongoDBSessionIDManager : SessionIDManager
{
private Random rnd = new Random();
private object oLock = new object(); public override string CreateSessionID(System.Web.HttpContext context)
{
int index = 0;
lock(this.oLock)
{
index = rnd.Next(SessionConfiguration.SessionServerIdentities.Length);
}
string sessionId = string.Format("{0}.{1}", SessionConfiguration.SessionServerIdentities[index], base.CreateSessionID(context));
return sessionId;
} public override string Encode(string id)
{
return DESEncryptor.Encode(id, SessionConfiguration.DESKey);
}
public override string Decode(string id)
{
return DESEncryptor.Decode(id, SessionConfiguration.DESKey);
} public override bool Validate(string id)
{
string prefix;
string realId; if (!Helper.ParseSessionID(id, out prefix, out realId))
return false; return base.Validate(realId);
}
}
MongoDBSessionStateStore
- 自定义Session过程中最核心的一个类,代码如下(较多):
public sealed class MongoDBSessionStateStore : SessionStateStoreProviderBase
{
private SessionStateSection pConfig;
private string pApplicationName; public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(name, config); pApplicationName =System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath;
System.Configuration.Configuration cfg = WebConfigurationManager.OpenWebConfiguration(pApplicationName);
pConfig =(SessionStateSection)cfg.GetSection("system.web/sessionState");
} public override SessionStateStoreData CreateNewStoreData(System.Web.HttpContext context, int timeout)
{
return new SessionStateStoreData(new SessionStateItemCollection(), SessionStateUtility.GetSessionStaticObjects(context), timeout);
} public override void CreateUninitializedItem(System.Web.HttpContext context, string id, int timeout)
{
//insert to db
MongoDBSessionEntity session = new MongoDBSessionEntity();
session.ApplicationName = this.pApplicationName;
session.SessionId = id;
session.Created = DateTime.Now;
session.Expires = DateTime.Now.AddMinutes(pConfig.Timeout.Minutes);
session.LockDate = DateTime.Now;
session.LockId = 0;
session.Timeout = timeout;
session.Locked = false;
session.Flags = (int)SessionStateActions.InitializeItem; MongoCollection<MongoDBSessionEntity> collection = Helper.GetMongoDBCollection(id);
collection.Save(session);
} public override void Dispose()
{
} public override void EndRequest(System.Web.HttpContext context)
{
} public override SessionStateStoreData GetItem(System.Web.HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
{
return GetSessionStoreItem(false, context, id, out locked, out lockAge, out lockId, out actions);
} public override SessionStateStoreData GetItemExclusive(System.Web.HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
{
return GetSessionStoreItem(true, context, id, out locked, out lockAge, out lockId, out actions);
} public override void InitializeRequest(System.Web.HttpContext context)
{
} public override void ReleaseItemExclusive(System.Web.HttpContext context, string id, object lockId)
{
//update locked=0, expired=, where lockId=?
MongoCollection<MongoDBSessionEntity> collection = Helper.GetMongoDBCollection(id); var query = Query.And( Query.EQ("LockId", int.Parse(lockId.ToString())),
Query.EQ("_id", id),
Query.EQ("ApplicationName", pApplicationName));
var update = Update.Set("Locked", false)
.Set("Expires", DateTime.Now.AddMinutes(pConfig.Timeout.Minutes)); collection.Update(query, update);
} public override void RemoveItem(System.Web.HttpContext context, string id, object lockId, SessionStateStoreData item)
{
//delete where sessionId=? and lockId=? and applicationname=?
MongoCollection<MongoDBSessionEntity> collection = Helper.GetMongoDBCollection(id); var query = Query.And(Query.EQ("LockId", int.Parse(lockId.ToString())),
Query.EQ("_id", id),
Query.EQ("ApplicationName", pApplicationName));
collection.Remove(query);
} public override void ResetItemTimeout(System.Web.HttpContext context, string id)
{
//update expire date
MongoCollection<MongoDBSessionEntity> collection = Helper.GetMongoDBCollection(id); var query = Query.And(Query.EQ("_id", id),
Query.EQ("ApplicationName", pApplicationName));
var update = Update.Set("Expires", DateTime.Now.AddMinutes(pConfig.Timeout.Minutes));
collection.Update(query, update);
} public override void SetAndReleaseItemExclusive(System.Web.HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem)
{
MongoCollection<MongoDBSessionEntity> collection = Helper.GetMongoDBCollection(id);
if (newItem)
{
//delete expired items
var query = Query.And(Query.EQ("_id", id),
Query.EQ("ApplicationName", pApplicationName),
Query.LT("Expires", DateTime.Now)); collection.Remove(query); //insert new item
MongoDBSessionEntity session = new MongoDBSessionEntity();
session.ApplicationName = this.pApplicationName;
session.SessionId = id;
session.Created = DateTime.Now;
session.Expires = DateTime.Now.AddMinutes(pConfig.Timeout.Minutes);
session.LockDate = DateTime.Now;
session.LockId = 0;
session.Timeout = item.Timeout;
session.Locked = false;
session.Flags = (int)SessionStateActions.None;
session.SessionItems = Helper.Serialize((SessionStateItemCollection)item.Items); collection.Save(session);
}
else
{
//update item
var query = Query.And(Query.EQ("_id", id),
Query.EQ("ApplicationName", pApplicationName),
Query.EQ("LockId", int.Parse(lockId.ToString())));
MongoDBSessionEntity entity= collection.FindOne(query);
entity.Expires = DateTime.Now.AddMinutes(item.Timeout);
entity.SessionItems = Helper.Serialize((SessionStateItemCollection)item.Items);
entity.Locked = false;
collection.Save(entity);
}
} public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback)
{
return false;
} private SessionStateStoreData GetSessionStoreItem(bool lockRecord, System.Web.HttpContext context,
string id,
out bool locked,
out TimeSpan lockAge,
out object lockId,
out SessionStateActions actions)
{
SessionStateStoreData item = null;
lockAge = TimeSpan.Zero;
lockId = null;
locked = false;
actions = 0; bool foundRecord = false;
bool deleteData = false; MongoCollection<MongoDBSessionEntity> collection = Helper.GetMongoDBCollection(id); if (lockRecord)
{
//update db, set locked=1, lockdate=now
var query1 = Query.And(Query.EQ("_id", id),
Query.EQ("ApplicationName", pApplicationName),
Query.EQ("Locked", MongoDB.Bson.BsonValue.Create(false)),
Query.GT("Expires", DateTime.UtcNow)); long count = collection.Find(query1).Count();
if (count == 0)
{
locked = true;
}
else
{
var update = Update.Set("Locked", true).Set("LockDate", DateTime.Now);
collection.Update(query1, update);
locked = false;
}
}
//get item by id
var query2 = Query.And(Query.EQ("_id", id),
Query.EQ("ApplicationName", pApplicationName));
MongoDBSessionEntity entity=collection.FindOne(query2);
if (entity != null)
{
if (entity.Expires < DateTime.Now)
{
locked = false;
deleteData = true;
}
else
{
foundRecord = true;
}
} //delete item if session expired
if (deleteData)
{
var query3 = Query.And(Query.EQ("_id", id),
Query.EQ("ApplicationName", pApplicationName));
collection.Remove(query3);
} if (!foundRecord)
locked = false; if (foundRecord && !locked)
{
if (lockId == null)
lockId = 0;
lockId = (int)lockId + 1; var query4 = Query.And(Query.EQ("_id", id),
Query.EQ("ApplicationName", pApplicationName));
var update4 = Update.Set("LockId", (int)lockId)
.Set("Flags", (int)SessionStateActions.None);
collection.Update(query4, update4); if (actions == SessionStateActions.InitializeItem)
item = CreateNewStoreData(context, pConfig.Timeout.Minutes);
else
item = Helper.Deserialize(context, entity.SessionItems, entity.Timeout);
}
return item;
}
}
由于很多方法会用到MongoCollection,因此写了个static公用函数,如下:

public static MongoCollection<MongoDBSessionEntity> GetMongoDBCollection(string sessionId)
{
IPartitionResolver resolver = new MongoDBSessionPartitionResolver();
string mongoDbConnectionString = resolver.ResolvePartition(sessionId); MongoClient client = new MongoClient(mongoDbConnectionString);
MongoServer srv = client.GetServer();
MongoDatabase db = srv.GetDatabase(SessionConfiguration.MongoDBName);
if (!db.CollectionExists(SessionConfiguration.MongoDBCollectionName))
db.CreateCollection(SessionConfiguration.MongoDBCollectionName); MongoCollection<MongoDBSessionEntity> collection = db.GetCollection<MongoDBSessionEntity>(SessionConfiguration.MongoDBCollectionName); return collection;
}

运行效果:
点击Set Session后:
点击Get Session后:
点击Abandon后:
源代码已经更新到A2D Framework中了。
基于MongoDB打造.Net的分布式Session子系统的更多相关文章
- SpringBoot搭建基于Apache Shiro+Redis的分布式Session共享功能
我们在上一遍文档中已经完成了Shiro验证功能.(http://www.cnblogs.com/nbfujx/p/7773789.html),在此基础上我们将完成分布式Session共享功能. Red ...
- .Net 基于Memcache集群的分布式Session
简述 基于Memcache的Session大家都各有各的说法,比方说:当memcached集群发生故障(比如内存溢出)或者维护(比如升级.增加或减少服务器)时,用户会无法登录,或者被踢掉线等等,每种技 ...
- 基于ZooKeeper的分布式Session实现(转)
1. 认识ZooKeeper ZooKeeper—— “动物园管理员”.动物园里当然有好多的动物,游客可以根据动物园提供的向导图到不同的场馆观赏各种类型的动物,而不是像走在原始丛林里,心惊胆颤的被 ...
- 基于ZooKeeper的分布式Session实现
1. 认识ZooKeeper ZooKeeper—— “动物园管理员”.动物园里当然有好多的动物,游客可以根据动物园提供的向导图到不同的场馆观赏各种类型的动物,而不是像走在原始丛林里,心惊胆颤的被 ...
- Java Web学习总结(20)——基于ZooKeeper的分布式session实现
1. 认识ZooKeeper ZooKeeper-- "动物园管理员".动物园里当然有好多的动物,游客可以根据动物园提供的向导图到不同的场馆观赏各种类型的动物,而不是像走在原始 ...
- ASP.NET WebApi 基于分布式Session方式实现Token签名认证
一.课程介绍 明人不说暗话,跟着阿笨一起学玩WebApi!开发提供数据的WebApi服务,最重要的是数据的安全性.那么对于我们来说,如何确保数据的安全将会是需要思考的问题.在ASP.NETWebSer ...
- ASP.NET WebApi 基于分布式Session方式实现Token签名认证(发布版)
一.课程介绍 明人不说暗话,跟着阿笨一起学玩WebApi!开发提供数据的WebApi服务,最重要的是数据的安全性.那么对于我们来说,如何确保数据的安全将会是需要思考的问题.在ASP.NETWebSer ...
- Tornado 自定义session,与一致性哈希 ,基于redis 构建分布式 session框架
Tornado 自定义session,与一致性哈希 ,基于redis 构建分布式 session import tornado.ioloop import tornado.web from myhas ...
- 分布式session的几种解决方案
现在很多商城,都会要求用户先去登录,登录之后再往购物车中添加商品,这样用户.购物车.商品,三个对象之间就有了绑定关系. 而针对我最开始说的那种情况,其实就是基于session做的,客户端往购物车中添加 ...
随机推荐
- C++ 对象模型具体评论(特别easy理解力)
c++对象模型系列 转 一.指针与引用 一 概括 指针和引用,在C++的软件开发中非经常见,假设能恰当的使用它们可以极大的提 高整个软件的效率,可是非常多的C++学习者对它们的各种使用情况并非都了解, ...
- .NET Framework 各版本区别
.NET Framework 各版本区别 .NET Framework 1.1 自1.0版本以来的改进:自带了对mobile asp .net控件的支持.这在1.0版本是以附加功能方式实现的,现在已经 ...
- sonp跨域请求
sonp跨域请求学习笔记 前言 ajax,用苍白的话赞扬:很好. 我们可以使用ajax实现异步获取数据,减少服务器运算时间,大大地改善用户体验:我们可以使用ajax实现小系统组合大系统:我们还可以 ...
- Bash shell 简单的并行任务,并等待
首先启动两个command line对于实验 第一 command line 依次输入: bash$ sleep 10001 & [1] 38272 bash$ job1=$! bash$ s ...
- 高速掌握sinox2014激动人心的ZFS和RAID技术
Sinox2014引入激动人心的zfs系统以及其支持的RAID,让用户高速打造便宜的高可靠性文件server. ZFS文件系统的英文名称为Zettabyte File System,也叫动态文件系统( ...
- Cocos2d-x3.0 TestCPP文件夹的注意事项
1.不多说了,重力加速度. 2.ActionMangerTest:此Test它是由导演来展示,以获得集体诉讼经理ActionManager类别,操作控制节点. ①CrashTest:破坏demo,毁. ...
- JS获取标签方法及兼容处理
document.getElementById('Id名'); // 所有浏览器 document.getElementsByTagName('标签名'); // 所有浏览器 document.ge ...
- 纯CSS3打造七巧板
原文:纯CSS3打造七巧板 最近项目上要制作一个七巧板,脑子里瞬间闪现,什么...七巧板不是小时候玩的吗... 七巧板的由来 先来个科普吧,是我在查资料过程中看到的,感觉很有意思. 宋朝有个叫黄伯思的 ...
- ExtJS4 自己定义基于配置的高级查询1
今天在编码过程中遇到一个问题,临时还没解决,先记录下来 上面是我做的高级查询面板..字段名和值都是读取配置文件,依据用户选择不同的字段名,自己主动载入不同的值列表,关系是与或 问题来了,我在字段名那个 ...
- DynamicResource与StaticResource的区别
原文:DynamicResource与StaticResource的区别 2008-06-20 12:16:12 静态资源在第一次编译后即确定其对象或值,之后不能对其进行修改.动态资源则是在运行时决定 ...