說一說sateless的那些事~

来源:http://www.cnblogs.com/shanshan27/archive/2016/09/21/5893215.html
-Advertisement-
Play Games

前段時間 工作需要 生平第一次聽到“無狀態”一詞 隨後 瞭解了下 怕自己忘記 隨手記錄下來 /* 尚未創建過Session:InitializeRequest -> CreateNewStoreData InitializeRequest -> ResetItemTimeout -> Initial ...


前段時間 工作需要 生平第一次聽到“無狀態”一詞 隨後 瞭解了下 怕自己忘記 隨手記錄下來 

/*

尚未創建過Session:
InitializeRequest -> CreateNewStoreData

InitializeRequest -> ResetItemTimeout -> InitializeRequest -> GetItemExclusive
(一種假象:http://localhost:29566和http://localhost:29566/Default.aspx)

 

已經創建過Session:

InitializeRequest -> GetItemExclusive
但如果沒查到 會 CreateUninitializedItem -> GetItemExclusive(不管經過是什麼,都會進入Page_Load)

 

--------------Page_load--------------
EndRequest(尚未創建過Session)
ReleaseItemExclusive -> EndRequest(已經創建過Session,此次無Update)
SetAndReleaseItemExclusive -> EndRequest (已經創建過Session,且此次有Update)

 


HttpModule -> RedisSession(InitializeRequest -> ResetItemTimeout只會在進入HttpHandle前調用這兩個方法) -> HttpHandle
不要再HttpModule、HttpHandle中操作Session
HttpModel是因為調用順序問題,HttpHandle是因為就算更新了,也不會進行被同步到Redis
好吧,其實這兩個的context.Session 都是null

 

Media資源(圖片、css、script)是不會進來的

 

*/

 

SessionStateStoreProviderBase類

定義一個數據存儲會話狀態所需的成員

Namespace:   System.Web.SessionState
Assembly:  System.Web (in System.Web.dll)

1.初始化每個請求對象

public abstract void InitializeRequest(HttpContext context)
Redis:
/// <summary>
        /// 在非同步回發初始化期間提出
        /// </summary>
        /// <param name="context"></param>
        public override void InitializeRequest(HttpContext context)
        {
            if (context != null && context.Request != null)
            {
                LogInfoFormat("InitializeRequest invoking: Cookie.ASP.NET_SessionId:{0},RequestRawUrl:{1}"
                    , context.Request.Cookies != null && context.Request.Cookies["ASP.NET_SessionId"] != null ? context.Request.Cookies["ASP.NET_SessionId"].Value : ""
                    , context.Request.RawUrl
                    );
            }
        }

 

2.創建一個新的對象用對當前的請求

public abstract SessionStateStoreData CreateNewStoreData(HttpContext context,int timeout)

Redis:
 public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout)
 {
   return new SessionStateStoreData(new SessionStateItemCollection(),staticObjectsGetter(context),timeout);
 }

 

3.更新此次會話的過期時間

public abstract void ResetItemTimeout(HttpContext context,string id)

Redis:

        public override void ResetItemTimeout(HttpContext context, string id)
        {
            //throw new NotImplementedException();
        }

4.從會話狀態存儲中返回只讀會話狀態

public abstract SessionStateStoreData GetItemExclusive(HttpContext context,string id,out bool locked,out TimeSpan lockAge,out object lockId,out SessionStateActions actions)

Redis:

public override SessionStateStoreData GetItemExclusive(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
        {
            LogInfoFormat("GetItemExclusive invoking: id:{0},RequestRawUrl:{1}"
                , id
                , context != null && context.Request != null ? context.Request.RawUrl : ""
                );
            //讀取redis,從redis獲取session
            return GetItemFromSessionStore(true, context, id, out locked, out lockAge, out lockId, out actions);
        }

/// <summary>
/// 從Redis讀取
/// </summary>
/// <param name="isWriteLockRequired"></param>
/// <param name="context"></param>
/// <param name="id"></param>
/// <param name="locked"></param>
/// <param name="lockAge"></param>
/// <param name="lockId"></param>
/// <param name="actions"></param>
/// <returns></returns>
private SessionStateStoreData GetItemFromSessionStore(bool isWriteLockRequired, HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
{
  locked = false;
  lockAge = TimeSpan.Zero;
  lockId = null;
  actions = SessionStateActions.None;
  SessionStateStoreData result = null;

  var key = GetSessionIdKey(id);

  var stateRaw = RedisHelper.RedisDB.HashGetAll(key);

  RedisSessionState state;
  if (!RedisSessionState.TryParse(stateRaw, out state))
  {
    locked = false; //如果在數據存儲區中未找到會話項數據,GetItemExclusive 方法就會將 locked out 參數設置為 false 並返回 null
    return null; //CreateUninitializedItem -> GetItemExclusive
  }

  actions = state.Flags;

  if (state.Locked)
  {
    locked = true; //如果在數據存儲區中找到會話項數據但是數據被鎖定,GetItemExclusive 方法就會將 locked out 參數設置為 true,將 lockAge out 參數設置為當前日期和時間與項被鎖定時的日期和時間(從數據存儲區中檢索)的差值,將 lockId out 參數設置為從數據存儲區中檢索的鎖定標識符,並返回 null
    lockId = state.LockId;
    //lockAge = new TimeSpan((DateTime.UtcNow - state.LockDate).Ticks * 7);//ExecutionTimeout有點長,100s左右
    lockAge = DateTime.UtcNow - state.LockDate;
    return null;
  }

  if (isWriteLockRequired)
  {
    locked = state.Locked = true;
    state.LockDate = DateTime.UtcNow;
    lockAge = TimeSpan.Zero;
    lockId = ++state.LockId;
  }

  state.Flags = SessionStateActions.None;

  UpdateSessionState(key, state);

  var items = actions == SessionStateActions.InitializeItem ? new SessionStateItemCollection() : state.Items;

  result = new SessionStateStoreData(items, staticObjectsGetter(context), state.Timeout);

  return result;
}

5.添加一個新的會話狀態項到會話存儲里

public abstract void CreateUninitializedItem(HttpContext context,string id,int timeout)

Redis:

// GetItemExclusive如果沒有查到,則會調用該方法(非因為Locked return null) 
public override void CreateUninitializedItem(HttpContext context, string id, int timeout)
        {
            var key = GetSessionIdKey(id);

            var state = new RedisSessionState()
            {
                Timeout = timeout,
                Flags = SessionStateActions.InitializeItem
            };

            UpdateSessionState(key, state);
        }
 private void UpdateSessionState(string key, RedisSessionState state)
  {
    UseTransaction(transaction =>
    {
      transaction.HashSetAsync(key, state.ToHashEntryArr());
      transaction.KeyExpireAsync(key, TimeSpan.FromMinutes(state.Timeout));
    });
  }

6.一個請求結束後回傳

public abstract void EndRequest(HttpContext context)

Redis:

        public override void EndRequest(HttpContext context)
        {
            //throw new NotImplementedException();
        }

7.釋放會話狀態存儲中的鎖定

public abstract void ReleaseItemExclusive(HttpContext context,string id,object lockId)

Redis:

  
///1、一次Http請求處理完之後(在Page_Load 及 aspx之後),如果沒有發生了Session Update,則會調用此方法
///2、Page_Load之前,如果反覆執行GetItemExclusive始終是locked狀態,一段時間之後系統會自行調用此方法來嘗試釋放.
///   如果 SessionStateModule 對象在調用 GetItemExclusive 或 GetItem 方法期間遇到鎖定的會話數據,它將以半秒為間隔重新請求會話數據,
/// 直到鎖定被釋放或者會話數據的鎖定時間超過 ExecutionTimeout 屬性的值。如果超過了執行超時時間,SessionStateModule 對象將調用 ReleaseItemExclusive 方法以釋放會話存儲區數據並隨即請求會話存儲區數據 public override void ReleaseItemExclusive(HttpContext context, string id, object lockId) { UnlockSessionStateIfLocked(id, (int)lockId); }      /// <summary> /// 釋放會話存儲區數據並隨即請求會話存儲區數據 /// </summary> /// <param name="id"></param> /// <param name="lockId"></param> private void UnlockSessionStateIfLocked(string id, int lockId) { var key = GetSessionIdKey(id); var stateRaw = RedisHelper.RedisDB.HashGetAll(key); RedisSessionState state; if (RedisSessionState.TryParse(stateRaw, out state) && state.Locked && state.LockId == lockId) { UnlockSessionState(key, state.Timeout); } } /// <summary> /// 不知道這樣 對比 UpdateSessionState, 性能怎麼樣。這裡雖然分成多項,但會作為一個transaction提交 /// </summary> /// <param name="client"></param> /// <param name="key"></param> /// <param name="state"></param> private void UnlockSessionState(string key, int timeout) { UseTransaction(transaction => { transaction.HashSetAsync(key, "locked", false); transaction.HashSetAsync(key, "lockId", 0); transaction.HashSetAsync(key, "lockDate", 0); //transaction.HashSetAsync(key, "timeout", timeout); //沒必要 transaction.KeyExpireAsync(key, TimeSpan.FromMinutes(timeout)); }); } /// <summary> /// /// </summary> /// <param name="action"></param> private void UseTransaction(Action<ITransaction> action) { try { //Redis的更新 都會進這 var transaction = RedisHelper.RedisDB.CreateTransaction(); action(transaction); transaction.Execute(); } catch (Exception ex) { LogError(ex.Message, ex); } }

8.在更新會話狀態數據存儲與當前請求的值的會話項信息,並清除對數據的鎖定

public abstract void SetAndReleaseItemExclusive(HttpContext context,string id,SessionStateStoreData item,object lockId,bool newItem)

Redis:

/// 一次Http請求處理完之後(在Page_Load 及 aspx之後),如果發生了Session Update,則會調用此方法
public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem)
        {
            if (newItem)  //true:定義一個新的狀態
            {
                var state = new RedisSessionState()
                {
                    Items = (SessionStateItemCollection)item.Items,
                    Timeout = item.Timeout,
                };

                var key = GetSessionIdKey(id);
                UpdateSessionState(key, state);
            }
            else //false 已存在的狀態
            {
                UpdateSessionStateIfLocked(id, (int)lockId, state =>
                {
                    state.Items = (SessionStateItemCollection)item.Items;
                    state.Locked = false;
                    state.Timeout = item.Timeout;
                });
            }

        }

     /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        /// <param name="lockId"></param>
        /// <param name="stateAction"></param>
        private void UpdateSessionStateIfLocked(string id, int lockId, Action<RedisSessionState> stateAction)
        {
            var key = GetSessionIdKey(id);

            var stateRaw = RedisHelper.RedisDB.HashGetAll(key);

            RedisSessionState state;

            if (RedisSessionState.TryParse(stateRaw, out state) && state.Locked && state.LockId == lockId)
            {
                stateAction(state);
                UpdateSessionState(key, state);
            }

        }

      /// <summary>
        /// 
        /// </summary>
        /// <param name="client"></param>
        /// <param name="key"></param>
        /// <param name="state"></param>
        private void UpdateSessionState(string key, RedisSessionState state)
        {
            UseTransaction(transaction =>
            {
                transaction.HashSetAsync(key, state.ToHashEntryArr());
                transaction.KeyExpireAsync(key, TimeSpan.FromMinutes(state.Timeout));
            });
        }
      /// <summary>
        /// 
        /// </summary>
        /// <param name="action"></param>
        private void UseTransaction(Action<ITransaction> action)
        {
            try
            {
                //Redis的更新 都會進這
                var transaction = RedisHelper.RedisDB.CreateTransaction();
                action(transaction);
                transaction.Execute();
            }
            catch (Exception ex)
            {
                LogError(ex.Message, ex);
            }
        }

 

 

 未完,待續...

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 多終端數據同步機制設計(二) Intro 如果您沒有看上一篇文章,建議您先移步到這裡查看第一部分 上一次主要解決了基本的數據增量同步的問題,但仍然存在一些問題。 可能存在的主要問題: 針對以上可能出現的這兩個問題,需要對數據進行校驗並且數據量超過一定量時進行分批量傳輸, 本文將著手解決 數據校驗 和 ...
  • 1.要安裝mvc3、mvc4、mvc5的插件 2.部署要創建應用程式池,並指定 3.許可權指定everyone ...
  • 遇到Unix時間戳轉換的問題,遂記錄下來。 Unix時間戳轉DateTime DateTime轉Unix時間戳 附:這是一個時間戳的轉換網址,上面也有不同語言的轉換。 http://unixtime.51240.com/ ...
  • 解決方法是新建 全局應用程式類 Global.asax 在 Session_Start 函數中 添加 string sessionId = Session.SessionID; ...
  • 王小明,他的長輩肯定有姓王的,彭河村村頭不就有家王姓人家嗎,可能就是那家的小孩,一個走丟的小孩就成功的回家了。 命名規範就如同給人起名一樣,從名字中傳達出一些信息,比如作用域、類型,能夠起到見名知義的作用,在開發的時候,有個良好的命名規範能夠提升不少的開發效率。特別是團隊開發的時候,每個人的命名規範 ...
  • 1、單行子查詢 select ename,deptno,sal from emp where deptno=(select deptno from dept where loc='NEW YORK'); 2、多行子查詢 SELECT ename,job,sal FROM EMP WHERE dept ...
  • 目錄 API 應用框架(Application Frameworks) 應用模板(Application Templates) 人工智慧(Artificial Intelligence) 程式集處理(Assembly Manipulation) 資源(Assets) 認證和授權(Authentica ...
  • 定義用來控制應用程式宿主環境的行為的配置設置。 配置如下 shadowCopyBinAssemblies:該值指示 Bin 目錄中的應用程式的程式集是否影像複製到該應用程式的 ASP.NET 臨時文件目錄中。但純看這句話我是一面懵懂的,幸虧看了一篇老外的文章經過自己實踐才明白其作用。平時我們更新bi ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...