zoukankan      html  css  js  c++  java
  • 基于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个类:

    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
     
  • 相关阅读:
    说说Java中的代理模式
    一个奇怪的异常
    JDBC第二次学习
    浅谈事务
    JDBC第一次学习
    Firebug & Chrome Console 控制台使用指南
    js 事件创建发布
    vue ui之 iview 事件拦截
    fetch获取json的正确姿势
    js对象通过属性路径获取属性值
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3288458.html
Copyright © 2011-2022 走看看