程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 如何基於MongoDB打造.Net的分布式Session子系統

如何基於MongoDB打造.Net的分布式Session子系統

編輯:關於.NET

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"/&

gt;
      </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;
        }

查看本欄目

點擊Get Session後:

點擊Abandon後:

源代碼已經更新到A2D Framework中了: https://sourceforge.net/projects/a2dframework/

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved