基于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个类:

  1. A2DFramework.SessionService.MongoDBSessionIDManager
  2. 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中了。

自省推动进步,视野决定未来。
心怀远大理想。
为了家庭幸福而努力。
A2D科技,服务社会。
A2D Framework
 

基于MongoDB打造.Net的分布式Session子系统的更多相关文章

  1. SpringBoot搭建基于Apache Shiro+Redis的分布式Session共享功能

    我们在上一遍文档中已经完成了Shiro验证功能.(http://www.cnblogs.com/nbfujx/p/7773789.html),在此基础上我们将完成分布式Session共享功能. Red ...

  2. .Net 基于Memcache集群的分布式Session

    简述 基于Memcache的Session大家都各有各的说法,比方说:当memcached集群发生故障(比如内存溢出)或者维护(比如升级.增加或减少服务器)时,用户会无法登录,或者被踢掉线等等,每种技 ...

  3. 基于ZooKeeper的分布式Session实现(转)

    1.   认识ZooKeeper ZooKeeper—— “动物园管理员”.动物园里当然有好多的动物,游客可以根据动物园提供的向导图到不同的场馆观赏各种类型的动物,而不是像走在原始丛林里,心惊胆颤的被 ...

  4. 基于ZooKeeper的分布式Session实现

    1.   认识ZooKeeper ZooKeeper—— “动物园管理员”.动物园里当然有好多的动物,游客可以根据动物园提供的向导图到不同的场馆观赏各种类型的动物,而不是像走在原始丛林里,心惊胆颤的被 ...

  5. Java Web学习总结(20)——基于ZooKeeper的分布式session实现

    1.   认识ZooKeeper ZooKeeper-- "动物园管理员".动物园里当然有好多的动物,游客可以根据动物园提供的向导图到不同的场馆观赏各种类型的动物,而不是像走在原始 ...

  6. ASP.NET WebApi 基于分布式Session方式实现Token签名认证

    一.课程介绍 明人不说暗话,跟着阿笨一起学玩WebApi!开发提供数据的WebApi服务,最重要的是数据的安全性.那么对于我们来说,如何确保数据的安全将会是需要思考的问题.在ASP.NETWebSer ...

  7. ASP.NET WebApi 基于分布式Session方式实现Token签名认证(发布版)

    一.课程介绍 明人不说暗话,跟着阿笨一起学玩WebApi!开发提供数据的WebApi服务,最重要的是数据的安全性.那么对于我们来说,如何确保数据的安全将会是需要思考的问题.在ASP.NETWebSer ...

  8. Tornado 自定义session,与一致性哈希 ,基于redis 构建分布式 session框架

    Tornado 自定义session,与一致性哈希 ,基于redis 构建分布式 session import tornado.ioloop import tornado.web from myhas ...

  9. 分布式session的几种解决方案

    现在很多商城,都会要求用户先去登录,登录之后再往购物车中添加商品,这样用户.购物车.商品,三个对象之间就有了绑定关系. 而针对我最开始说的那种情况,其实就是基于session做的,客户端往购物车中添加 ...

随机推荐

  1. Linux线程 之 线程 线程组 进程 轻量级进程(LWP)

    Thread Local Storage,线程本地存储,大神Ulrich Drepper有篇PDF文档是讲TLS的,我曾经努力过三次尝试搞清楚TLS的原理,均没有彻底搞清楚.这一次是第三次,我沉浸gl ...

  2. .net EF 事物 订单流水号的生成 (二):观察者模式、事物、EF

    针对.net EF 事物 订单流水号的生成 (一)  的封装. 数据依然不变. using System; using System.Linq; using System.Transactions; ...

  3. ASP.NET MVC中加载WebForms用户控件(.ascx)

    原文:ASP.NET MVC中加载WebForms用户控件(.ascx) 问题背景 博客园博客中的日历用的是ASP.NET WebForms的日历控件(System.Web.UI.WebControl ...

  4. VirtualBox更改虚拟机磁盘VDI的大小

    流程虚拟机中使用,有时会遇到磁盘大小是不够的,假设一套"动态分配的内存".通过下面的方法来手动扩展磁盘空间. 1.启动CMD命令行.进入VirtualBox安装文件夹.例如 cd  ...

  5. SOD框架的数据容器,打造最适合DDD的ORM框架

    SOD框架的数据容器,打造最适合DDD的ORM框架 引言:DDD的困惑 最近,我看到园子里面有位朋友的一篇博客 <领域驱动设计系列(一):为何要领域驱动设计? >文章中有下面一段话,对DD ...

  6. 一步一步写算法(之prim算法 下)

    原文:一步一步写算法(之prim算法 下) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 前两篇博客我们讨论了prim最小生成树的算法,熟悉 ...

  7. JS模块与命名空间的介绍

    起因将代码组织到类中的一个重要原因是让代码更加“模块化”,可以在很多不同的场景中实现代码的重用.但类不是唯一的模块化代码的方式. 一般来讲,模块是一个独立的JS文件.模块文件可以包含一个类定义.一组相 ...

  8. SQL 2005 中查询或执行另外的数据库操作的方法

    原文:SQL 2005 中查询或执行另外的数据库操作的方法 摘要: 如果,你想在一台数据库服务器上,查询另一个台数据服务器的数据该如何做呢?如果,你想在同一台数据服务器上,在不同的数据库之间查询数据, ...

  9. Spring IOC 之Bean作用域

    当你创建一个bean定义的时候,你创建了一份通过那种bean定义的bean的创建类的真正实力的处方.bean的定义是一个处方 的想法是很重要的的.因为这意味着,对于一个类你可以创建很多对象实例从一个单 ...

  10. C# & WPF 随手小记之一 ——初探async await 实现多线程处理

    嗯...我也是在园子待了不短时间的人了,一直以来汲取着园友的知识,感觉需要回馈什么. 于是以后有空我都会把一些小技巧小知识写下来,有时候可能会很短甚至很简单,但希望能帮到大家咯. 第一篇文章来说说as ...