基于MongoDB打造.Net的分布式Session子系统

Taobao有她自己的分布式session框架,.net阵营也不能落后了,在下做了个基于MongoDB的支持最多26台MongoDB的分布式Session框架。

先看看配置文件:

  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <MongoDBSession>
  3. <DbName>SessionDB</DbName>
  4. <IdentityMap Identity="A">mongodb://localhost</IdentityMap>
  5. <IdentityMap Identity="B">mongodb://localhost</IdentityMap>
  6. <IdentityMap Identity="C">mongodb://localhost</IdentityMap>
  7. <IdentityMap Identity="D">mongodb://localhost</IdentityMap>
  8. <IdentityMap Identity="E">mongodb://localhost</IdentityMap>
  9. <IdentityMap Identity="F">mongodb://localhost</IdentityMap>
  10. <IdentityMap Identity="G">mongodb://localhost</IdentityMap>
  11. <IdentityMap Identity="H">mongodb://localhost</IdentityMap>
  12. <IdentityMap Identity="I">mongodb://localhost</IdentityMap>
  13. <IdentityMap Identity="J">mongodb://localhost</IdentityMap>
  14. <IdentityMap Identity="K">mongodb://localhost</IdentityMap>
  15. <IdentityMap Identity="L">mongodb://localhost</IdentityMap>
  16. <IdentityMap Identity="M">mongodb://localhost</IdentityMap>
  17. <IdentityMap Identity="N">mongodb://localhost</IdentityMap>
  18. <IdentityMap Identity="O">mongodb://localhost</IdentityMap>
  19. <IdentityMap Identity="P">mongodb://localhost</IdentityMap>
  20. <IdentityMap Identity="Q">mongodb://localhost</IdentityMap>
  21. <IdentityMap Identity="R">mongodb://localhost</IdentityMap>
  22. <IdentityMap Identity="S">mongodb://localhost</IdentityMap>
  23. <IdentityMap Identity="T">mongodb://localhost</IdentityMap>
  24. <IdentityMap Identity="U">mongodb://localhost</IdentityMap>
  25. <IdentityMap Identity="V">mongodb://localhost</IdentityMap>
  26. <IdentityMap Identity="W">mongodb://localhost</IdentityMap>
  27. <IdentityMap Identity="X">mongodb://localhost</IdentityMap>
  28. <IdentityMap Identity="Y">mongodb://localhost</IdentityMap>
  29. <IdentityMap Identity="Z">mongodb://localhost</IdentityMap>
  30. </MongoDBSession>

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

  1. protected void btnTest_Click(object sender, EventArgs e)
  2. {
  3. Session["A"] = DateTime.Now;
  4. Session["B"] = 1111111111111;
  5. Session["C"] = "fffffffffffffff";
  6. }
  7.  
  8. protected void btnGetSession_Click(object sender, EventArgs e)
  9. {
  10. Response.Write(Session["A"].ToString());
  11. Response.Write("<br />");
  12. Response.Write(Session["B"].ToString());
  13. Response.Write("<br />");
  14. Response.Write(Session["C"].ToString());
  15. }
  16. protected void btnAbandon_Click(object sender, EventArgs e)
  17. {
  18. Session.Abandon();
  19. }

呵呵,就是普通的Session用法。

这个要配置web.config:

  1. <system.web>
  2. <sessionState mode="Custom" customProvider="A2DSessionProvider" sessionIDManagerType="A2DFramework.SessionService.MongoDBSessionIDManager">
  3. <providers>
  4. <add name="A2DSessionProvider" type="A2DFramework.SessionService.MongoDBSessionStateStore"/>
  5. </providers>
  6. </sessionState>
  7. </system.web>

这里会牵扯出2个类:

  1. A2DFramework.SessionService.MongoDBSessionIDManager
  2. A2DFramework.SessionService.MongoDBSessionStateStore

MongoDBSessionIDManager

  • 自定义生成的cookie值(也就是SessionID),在这个sample中,会生成如“E.asadfalkasdfjal”这样的SessionID,其中前缀E代表这个Session的信息会映射到哪台MongoDB上。
  • 关键代码
    1. public class MongoDBSessionIDManager : SessionIDManager
    2. {
    3. private Random rnd = new Random();
    4. private object oLock = new object();
    5.  
    6. public override string CreateSessionID(System.Web.HttpContext context)
    7. {
    8. int index = 0;
    9. lock(this.oLock)
    10. {
    11. index = rnd.Next(SessionConfiguration.SessionServerIdentities.Length);
    12. }
    13. string sessionId = string.Format("{0}.{1}", SessionConfiguration.SessionServerIdentities[index], base.CreateSessionID(context));
    14. return sessionId;
    15. }
    16.  
    17. public override string Encode(string id)
    18. {
    19. return DESEncryptor.Encode(id, SessionConfiguration.DESKey);
    20. }
    21. public override string Decode(string id)
    22. {
    23. return DESEncryptor.Decode(id, SessionConfiguration.DESKey);
    24. }
    25.  
    26. public override bool Validate(string id)
    27. {
    28. string prefix;
    29. string realId;
    30.  
    31. if (!Helper.ParseSessionID(id, out prefix, out realId))
    32. return false;
    33.  
    34. return base.Validate(realId);
    35. }
    36. }

MongoDBSessionStateStore

  • 自定义Session过程中最核心的一个类,代码如下(较多):
    1. public sealed class MongoDBSessionStateStore : SessionStateStoreProviderBase
    2. {
    3. private SessionStateSection pConfig;
    4. private string pApplicationName;
    5.  
    6. public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
    7. {
    8. base.Initialize(name, config);
    9.  
    10. pApplicationName =System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath;
    11. System.Configuration.Configuration cfg = WebConfigurationManager.OpenWebConfiguration(pApplicationName);
    12. pConfig =(SessionStateSection)cfg.GetSection("system.web/sessionState");
    13. }
    14.  
    15. public override SessionStateStoreData CreateNewStoreData(System.Web.HttpContext context, int timeout)
    16. {
    17. return new SessionStateStoreData(new SessionStateItemCollection(), SessionStateUtility.GetSessionStaticObjects(context), timeout);
    18. }
    19.  
    20. public override void CreateUninitializedItem(System.Web.HttpContext context, string id, int timeout)
    21. {
    22. //insert to db
    23. MongoDBSessionEntity session = new MongoDBSessionEntity();
    24. session.ApplicationName = this.pApplicationName;
    25. session.SessionId = id;
    26. session.Created = DateTime.Now;
    27. session.Expires = DateTime.Now.AddMinutes(pConfig.Timeout.Minutes);
    28. session.LockDate = DateTime.Now;
    29. session.LockId = 0;
    30. session.Timeout = timeout;
    31. session.Locked = false;
    32. session.Flags = (int)SessionStateActions.InitializeItem;
    33.  
    34. MongoCollection<MongoDBSessionEntity> collection = Helper.GetMongoDBCollection(id);
    35. collection.Save(session);
    36. }
    37.  
    38. public override void Dispose()
    39. {
    40. }
    41.  
    42. public override void EndRequest(System.Web.HttpContext context)
    43. {
    44. }
    45.  
    46. public override SessionStateStoreData GetItem(System.Web.HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
    47. {
    48. return GetSessionStoreItem(false, context, id, out locked, out lockAge, out lockId, out actions);
    49. }
    50.  
    51. public override SessionStateStoreData GetItemExclusive(System.Web.HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
    52. {
    53. return GetSessionStoreItem(true, context, id, out locked, out lockAge, out lockId, out actions);
    54. }
    55.  
    56. public override void InitializeRequest(System.Web.HttpContext context)
    57. {
    58. }
    59.  
    60. public override void ReleaseItemExclusive(System.Web.HttpContext context, string id, object lockId)
    61. {
    62. //update locked=0, expired=, where lockId=?
    63. MongoCollection<MongoDBSessionEntity> collection = Helper.GetMongoDBCollection(id);
    64.  
    65. var query = Query.And( Query.EQ("LockId", int.Parse(lockId.ToString())),
    66. Query.EQ("_id", id),
    67. Query.EQ("ApplicationName", pApplicationName));
    68. var update = Update.Set("Locked", false)
    69. .Set("Expires", DateTime.Now.AddMinutes(pConfig.Timeout.Minutes));
    70.  
    71. collection.Update(query, update);
    72. }
    73.  
    74. public override void RemoveItem(System.Web.HttpContext context, string id, object lockId, SessionStateStoreData item)
    75. {
    76. //delete where sessionId=? and lockId=? and applicationname=?
    77. MongoCollection<MongoDBSessionEntity> collection = Helper.GetMongoDBCollection(id);
    78.  
    79. var query = Query.And(Query.EQ("LockId", int.Parse(lockId.ToString())),
    80. Query.EQ("_id", id),
    81. Query.EQ("ApplicationName", pApplicationName));
    82. collection.Remove(query);
    83. }
    84.  
    85. public override void ResetItemTimeout(System.Web.HttpContext context, string id)
    86. {
    87. //update expire date
    88. MongoCollection<MongoDBSessionEntity> collection = Helper.GetMongoDBCollection(id);
    89.  
    90. var query = Query.And(Query.EQ("_id", id),
    91. Query.EQ("ApplicationName", pApplicationName));
    92. var update = Update.Set("Expires", DateTime.Now.AddMinutes(pConfig.Timeout.Minutes));
    93. collection.Update(query, update);
    94. }
    95.  
    96. public override void SetAndReleaseItemExclusive(System.Web.HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem)
    97. {
    98. MongoCollection<MongoDBSessionEntity> collection = Helper.GetMongoDBCollection(id);
    99. if (newItem)
    100. {
    101. //delete expired items
    102. var query = Query.And(Query.EQ("_id", id),
    103. Query.EQ("ApplicationName", pApplicationName),
    104. Query.LT("Expires", DateTime.Now));
    105.  
    106. collection.Remove(query);
    107.  
    108. //insert new item
    109. MongoDBSessionEntity session = new MongoDBSessionEntity();
    110. session.ApplicationName = this.pApplicationName;
    111. session.SessionId = id;
    112. session.Created = DateTime.Now;
    113. session.Expires = DateTime.Now.AddMinutes(pConfig.Timeout.Minutes);
    114. session.LockDate = DateTime.Now;
    115. session.LockId = 0;
    116. session.Timeout = item.Timeout;
    117. session.Locked = false;
    118. session.Flags = (int)SessionStateActions.None;
    119. session.SessionItems = Helper.Serialize((SessionStateItemCollection)item.Items);
    120.  
    121. collection.Save(session);
    122. }
    123. else
    124. {
    125. //update item
    126. var query = Query.And(Query.EQ("_id", id),
    127. Query.EQ("ApplicationName", pApplicationName),
    128. Query.EQ("LockId", int.Parse(lockId.ToString())));
    129. MongoDBSessionEntity entity= collection.FindOne(query);
    130. entity.Expires = DateTime.Now.AddMinutes(item.Timeout);
    131. entity.SessionItems = Helper.Serialize((SessionStateItemCollection)item.Items);
    132. entity.Locked = false;
    133. collection.Save(entity);
    134. }
    135. }
    136.  
    137. public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback)
    138. {
    139. return false;
    140. }
    141.  
    142. private SessionStateStoreData GetSessionStoreItem(bool lockRecord, System.Web.HttpContext context,
    143. string id,
    144. out bool locked,
    145. out TimeSpan lockAge,
    146. out object lockId,
    147. out SessionStateActions actions)
    148. {
    149. SessionStateStoreData item = null;
    150. lockAge = TimeSpan.Zero;
    151. lockId = null;
    152. locked = false;
    153. actions = 0;
    154.  
    155. bool foundRecord = false;
    156. bool deleteData = false;
    157.  
    158. MongoCollection<MongoDBSessionEntity> collection = Helper.GetMongoDBCollection(id);
    159.  
    160. if (lockRecord)
    161. {
    162. //update db, set locked=1, lockdate=now
    163. var query1 = Query.And(Query.EQ("_id", id),
    164. Query.EQ("ApplicationName", pApplicationName),
    165. Query.EQ("Locked", MongoDB.Bson.BsonValue.Create(false)),
    166. Query.GT("Expires", DateTime.UtcNow));
    167.  
    168. long count = collection.Find(query1).Count();
    169. if (count == 0)
    170. {
    171. locked = true;
    172. }
    173. else
    174. {
    175. var update = Update.Set("Locked", true).Set("LockDate", DateTime.Now);
    176. collection.Update(query1, update);
    177. locked = false;
    178. }
    179. }
    180. //get item by id
    181. var query2 = Query.And(Query.EQ("_id", id),
    182. Query.EQ("ApplicationName", pApplicationName));
    183. MongoDBSessionEntity entity=collection.FindOne(query2);
    184. if (entity != null)
    185. {
    186. if (entity.Expires < DateTime.Now)
    187. {
    188. locked = false;
    189. deleteData = true;
    190. }
    191. else
    192. {
    193. foundRecord = true;
    194. }
    195. }
    196.  
    197. //delete item if session expired
    198. if (deleteData)
    199. {
    200. var query3 = Query.And(Query.EQ("_id", id),
    201. Query.EQ("ApplicationName", pApplicationName));
    202. collection.Remove(query3);
    203. }
    204.  
    205. if (!foundRecord)
    206. locked = false;
    207.  
    208. if (foundRecord && !locked)
    209. {
    210. if (lockId == null)
    211. lockId = 0;
    212. lockId = (int)lockId + 1;
    213.  
    214. var query4 = Query.And(Query.EQ("_id", id),
    215. Query.EQ("ApplicationName", pApplicationName));
    216. var update4 = Update.Set("LockId", (int)lockId)
    217. .Set("Flags", (int)SessionStateActions.None);
    218. collection.Update(query4, update4);
    219.  
    220. if (actions == SessionStateActions.InitializeItem)
    221. item = CreateNewStoreData(context, pConfig.Timeout.Minutes);
    222. else
    223. item = Helper.Deserialize(context, entity.SessionItems, entity.Timeout);
    224. }
    225. return item;
    226. }
    227. }

由于很多方法会用到MongoCollection,因此写了个static公用函数,如下:

  1. public static MongoCollection<MongoDBSessionEntity> GetMongoDBCollection(string sessionId)
  2. {
  3. IPartitionResolver resolver = new MongoDBSessionPartitionResolver();
  4. string mongoDbConnectionString = resolver.ResolvePartition(sessionId);
  5.  
  6. MongoClient client = new MongoClient(mongoDbConnectionString);
  7. MongoServer srv = client.GetServer();
  8. MongoDatabase db = srv.GetDatabase(SessionConfiguration.MongoDBName);
  9. if (!db.CollectionExists(SessionConfiguration.MongoDBCollectionName))
  10. db.CreateCollection(SessionConfiguration.MongoDBCollectionName);
  11.  
  12. MongoCollection<MongoDBSessionEntity> collection = db.GetCollection<MongoDBSessionEntity>(SessionConfiguration.MongoDBCollectionName);
  13.  
  14. return collection;
  15. }

运行效果:

点击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. 算法题C#

    几个大公司(IBM.MicroSoft and so on)面试经典数据结构与算法题C#解答 1.链表反转 我想到了两种比较简单的方法 第一种是需要开一个新的链表,将原链表的元素从后到前的插入到新链表 ...

  2. 在面对变化,撇开NO

    参观后转到供应商,看到自己的生产线流水线半自己的钣金生产线举措.这就是我一直想厂提高生产现场的想法,因为通常当我看到工作人员努力工作和繁忙的生产,只见废现场,线解决方式时,有点莫名的兴奋. 幸亏是一家 ...

  3. Java数据结构与算法(3) - ch04栈(栈和转置)

    栈的基本特性是后进先出,最简单的用途是用于转置,还有其他诸如括号匹配,中序表达式(A+B*(C-D/(E+F)) --> ABCDEF+/-*+)和后续表达式(345+*612+/- --> ...

  4. centos 7安装源

    参照 http://www.linuxidc.com/Linux/2015-03/114690.htm http://www.cnblogs.com/mchina/archive/2013/01/04 ...

  5. PHP Warning: strtotime(): It is not safe to rely on the system's timezone settings.

    有三种解决办法: 1. php文件中设置时区 <?php date_default_timezone_set('Asia/Shanghai'); echo strtotime('2012-9-3 ...

  6. 生产环境使用Nginx+uwsgi部署Django

    在本地运行django应用相对来说还是挺方便的,使用自带的runserver启动即可.如果在生产环境部署django,就要多考虑一些问题了.比如静态文件处理,安全,效率等等 在网上找到了不错的部署的教 ...

  7. Erlang运行中的错误

    Erlang运行时发生错误时,会返回一些错误信息,理解这些信息,对于学好.用好Erlang来说是必要. Erlang中的运行错误包括:badarg, badarith, badmatch, funct ...

  8. Office文档在线编辑的实现之二

    讲述了如何通过iis的webdav支持实现客户端的office直接编辑服务器上的文件,本篇将讲解如何实现客户端的office直接编辑数据库中的二进制形式保存的office文件. 实现的关键:模拟IIS ...

  9. .Net下简单地实现MD5加密

    在.Net中为我们提供了一个方法HashPasswordForStoringInConfigFile,可以简单方便地实现MD5加密.该方法位于System.Web命名空间下,所以需要在引用中添加Sys ...

  10. 【转】android webview设置内容的字体大小

    Enum for specifying the text size. SMALLEST is 50% SMALLER is 75% NORMAL is 100% LARGER is 150% LARG ...