< 返回技术文档列表

如何利用MongoDB打造.Net的分布式Session子系统

发布时间:2021-11-07 01:26:16

本篇文章为大家展示了如何利用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;          }

运行效果:

如何利用MongoDB打造.Net的分布式Session子系统

点击Set Session后:

如何利用MongoDB打造.Net的分布式Session子系统

点击Get Session后:

如何利用MongoDB打造.Net的分布式Session子系统

点击Abandon后:

如何利用MongoDB打造.Net的分布式Session子系统

源代码已经更新到A2D Framework中了。

上述内容就是如何利用MongoDB打造.Net的分布式Session子系统,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注血鸟云行业资讯频道。


/template/Home/Zkeys/PC/Static