首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> 關於.NET

.NET 分散式 Session 子系統:基於 MongoDB 實現 26 節點水平擴展

     編輯:關於.NET

前言

ASP.NET 預設 InProc Session 無法支援 Web Farm。本文實作基於 MongoDB 的自訂 Session State Provider,支援最多 26 台 MongoDB 節點 (A-Z 分區),透過 SessionID 前綴自動路由,完全相容標準 Session 物件操作介面。

組態設定:MongoDBSession.config

<?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>
    <!-- ... 至 Z -->
    <IdentityMap Identity="Z">mongodb://localhost</IdentityMap>
</MongoDBSession>
  • 26 個 Identity (A-Z) 對應 26 個 MongoDB 連接字串
  • 生產環境可對應不同實體伺服器/分片

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>

核心類別 1:MongoDBSessionIDManager - 自訂 SessionID 生成與解析


public class MongoDBSessionIDManager : SessionIDManager
{
    private Random rnd = new Random();
    private object oLock = new object();

    public override string CreateSessionID(HttpContext context)
    {
        int index;
        lock (oLock) { index = rnd.Next(SessionConfiguration.SessionServerIdentities.Length); }
        // 格式: "E.{base64_guid}" 前綴 E 表示對應 IdentityMap E
        return string.Format("{0}.{1}", SessionConfiguration.SessionServerIdentities[index], base.CreateSessionID(context));
    }

    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, realId;
        if (!Helper.ParseSessionID(id, out prefix, out realId)) return false;
        return base.Validate(realId);
    }
}

核心類別 2:MongoDBSessionStateStore - Session 狀態存取完整實作


public sealed class MongoDBSessionStateStore : SessionStateStoreProviderBase
{
    private SessionStateSection pConfig;
    private string pApplicationName;

    public override void Initialize(string name, NameValueCollection config)
    {
        base.Initialize(name, config);
        pApplicationName = HostingEnvironment.ApplicationVirtualPath;
        var cfg = WebConfigurationManager.OpenWebConfiguration(pApplicationName);
        pConfig = (SessionStateSection)cfg.GetSection("system.web/sessionState");
    }

    public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout)
    {
        return new SessionStateStoreData(new SessionStateItemCollection(), 
            SessionStateUtility.GetSessionStaticObjects(context), timeout);
    }

    // 建立未初始化項目
    public override void CreateUninitializedItem(HttpContext context, string id, int timeout)
    {
        var session = new MongoDBSessionEntity { /* 初始化屬性 */ };
        Helper.GetMongoDBCollection(id).Save(session);
    }

    // 讀取 (非獨佔)
    public override SessionStateStoreData GetItem(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(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 ReleaseItemExclusive(HttpContext context, string id, object lockId)
    {
        var 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(HttpContext context, string id, object lockId, SessionStateStoreData item)
    {
        var 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(HttpContext context, string id)
    {
        var 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(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem)
    {
        var collection = Helper.GetMongoDBCollection(id);
        if (newItem)
        {
            // 移除過期項目後插入新項目
        }
        else
        {
            // 更新現有項目
        }
    }

    private SessionStateStoreData GetSessionStoreItem(bool lockRecord, HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
    {
        // 完整的鎖定檢查、資料讀取、反序列化邏輯
    }
}

分區解析與集合存取


public static MongoCollection GetMongoDBCollection(string sessionId)
{
    IPartitionResolver resolver = new MongoDBSessionPartitionResolver();
    string mongoDbConnectionString = resolver.ResolvePartition(sessionId); // 依前綴解析
    
    var client = new MongoClient(mongoDbConnectionString);
    var server = client.GetServer();
    var db = server.GetDatabase(SessionConfiguration.MongoDBName);
    
    if (!db.CollectionExists(SessionConfiguration.MongoDBCollectionName))
        db.CreateCollection(SessionConfiguration.MongoDBCollectionName);
    
    return db.GetCollection(SessionConfiguration.MongoDBCollectionName);
}

標準 Session 使用方式 (零學習成本)


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"] + "
"); Response.Write(Session["B"] + "
"); Response.Write(Session["C"] + "
"); } protected void btnAbandon_Click(object sender, EventArgs e) { Session.Abandon(); }

執行結果展示

取得 Session 值畫面

Abandon 後畫面

架構優勢

  • 水平擴展:26 節點線性擴充寫入/讀取吞吐量
  • 透明相容:既有代碼零修改,標準 Session API
  • 高可用:MongoDB Replica Set 自動故障轉移
  • 安全性:SessionID 加密 (DES) + 前綴路由邏輯分離
  • 效能:原生 BSON 序列化、索引優化

原始碼整合於 A2D Framework:sourceforge.net/projects/a2dframework/

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved