<form id="hz9zz"></form>
  • <form id="hz9zz"></form>

      <nobr id="hz9zz"></nobr>

      <form id="hz9zz"></form>

    1. 明輝手游網中心:是一個免費提供流行視頻軟件教程、在線學習分享的學習平臺!

      ASP.NET 中 Session 完成原理淺析 [2] 狀態管理器

      [摘要]狀態管理本來是一件很美好的事情,嘿嘿,只可惜總是有些廠商在實現的時候考慮得不那么周全。例如 MS 在 ASP 中的狀態管理實現就比較爛,因為只實現了一個進程內的基于內存的狀態管理,故而存在很多問題:...
      狀態管理本來是一件很美好的事情,嘿嘿,只可惜總是有些廠商在實現的時候考慮得不那么周全。例如 MS 在 ASP 中的狀態管理實現就比較爛,因為只實現了一個進程內的基于內存的狀態管理,故而存在很多問題:

      1.所有的 Session 數據都保存在 Web 服務的進程中,會造成服務器支持會話數量受到服務器內存資源的限制問題,同時也因為大量非活動會話導致內存被無效占用。
      2.服務器進程崩潰會導致所有的會話數據丟失。
      3.會話無法跨進程或在負載均衡情況下使用,除非負載均衡技術保障同一用戶每次都能被路由到同一機器上。就算這樣也無法保障服務器崩潰造成的會話數據丟失。
      4.需要 Cookie 的支持,而現在因為安全性問題,很多人在瀏覽器中關閉了 Cookie 和 js 的支持。

      為此 ASP 的使用者不得不自己手工將會話信息以會話 ID 為主鍵同步到外部數據庫中,以緩解類似問題。

      而在 ASP.NET 中,因為設計時就考慮了這些問題,能夠避免這些限制:

      1.支持進程外的狀態管理,通過獨立狀態管理服務或 SQL Server 狀態服務器管理會話狀態
      2.支持不使用 Cookie 的狀態維護,通過在 URL 中自動增加會話 ID 來避免使用 Cookie
      3.通過獨立的狀態管理服務或SQL Server 狀態服務器支持負載均衡時同步使用會話信息

      實現這些特性的正是上節提到的 SessionStateModule.InitModuleFromConfig 函數中,根據 sessionState 標記的 mode 屬性選擇的四種不同的狀態管理器實現。


      以下內容為程序代碼:

      <system.web>
      <sessionState mode="InProc"
      stateConnectionString="tcpip=127.0.0.1:42424"
      stateNetworkTimeout="10"
      sqlConnectionString="data source=127.0.0.1;Integrated Security=SSPI"
      cookieless="false"
      timeout="20" />
      </system.web>



      Off 模式禁止會話管理,同時 ASP.NET 還允許通過在頁面中以 EnableSessionState 屬性細粒度管理頁面的會話支持狀態
      以下內容為程序代碼:

      <%@ Page EnableSessionState=" True False ReadOnly" %>



      InProc 模式兼容以前 ASP 的策略,在 ASP.NET 同一進程空間內實現基于內存的會話狀態管理,速度最快但受到與 ASP 相同的限制;
      StateServer 模式通過 ASP.NET 獨立安裝的 ASP.NET State Service 服務(aspnet_state.exe),以 stateConnectionString 指定的IP和端口響應會話狀態服務;
      SQLServer 模式則通過 sqlConnectionString 指定的 SQL Server 服務器,以內存臨時表(以 InstallSqlState.sql建庫,使用 tempdb 內存數據庫)或獨立表(以InstallPersistSqlState.sql 監控,使用獨立的 ASPState 庫)維護會話狀態。

      這四種不同的狀態管理器,在性能上據《Performance Tuning and Optimizing ASP.NET Appliation》一書的測試,相對值如下:

      以下為引用:

      Table 4-1: Normalized TTLB(Time to Last Byte) by Session State Mode (in Milliseconds per 100 Requests)

      CONCURRENT BROWSERS MODE = OFF MODE = INPROC MODE = STATESERVER MODE = SQLSERVER
      1 7.81 4.54 8.27 8.47
      5 28.28 20.25 27.25 29.29
      10 89.38 46.08 77.29 85.11


      Table 4-2: Average Requests per Second by Session State Mode

      CONCURRENT BROWSERS MODE = OFF MODE = INPROC MODE = STATESERVER MODE = SQLSERVER

      1 18.86 24.17 18.31 18.11
      5 21.66 25.74 21.54 21.34
      10 17.23 23.8 18.11 17.6




      可以看到,無論是從 TTLB 還是每秒平均請求數來說,進程外狀態管理器的性能都是可以令人接受的,當然還需要針對狀態管理情況在編寫代碼時做相關優化。不過要使用進程外狀態管理器,則保存在會話中的對象受到必須提高二進制序列化支持的限制。

      從使用角度來看,狀態管理器實際上都是由上節提到的 HttpSessionModule 建立管理,并通過 HttpSessionState 接口提供訪問的,結構如下圖:


      MSDN 上的 Underpinnings of the Session State Implementation in ASP.NET 一文非常詳細的解釋了幾種不同狀態管理器的原理和使用,這兒就不羅嗦了。

      從實現角度來看,上節中提到的 SessionStateModule.InitModuleFromConfig 函數,根據配置文件中狀態管理器的模式,分別建立 System.Web.SessionState.InProcStateClientManager, System.Web.SessionState.OutOfProcStateClientManager 和 System.Web.SessionState.SqlStateClientManager 三類狀態管理器的實例。他們都繼承自 System.Web.SessionState.StateClientManager 抽象基類,并通過 System.Web.SessionState.IStateClientManager 接口向 HttpApplication 提高狀態管理服務。

      IStateClientManager 接口是狀態管理器的統一管理接口,主要提供以下功能:
      以下內容為程序代碼:

      internal interface System.Web.SessionState.IStateClientManager.IStateClientManager
      {
      // 配置管理狀態管理器
      void ConfigInit(SessionStateSectionHandler.Config config, SessionOnEndTarget onEndTarget);
      // 保存 SessionStateModule 實例供后面使用
      void SetStateModule(SessionStateModule module);
      void ResetTimeout(string id);
      void Dispose();

      void Set(string id, SessionStateItem item, bool inStorage);

      // 維護狀態管理器內容
      IAsyncResult BeginGet(string id, AsyncCallback cb, object state);
      SessionStateItem EndGet(IAsyncResult ar);

      IAsyncResult BeginGetExclusive(string id, AsyncCallback cb, object state);
      SessionStateItem EndGetExclusive(IAsyncResult ar);
      void ReleaseExclusive(string id, int lockCookie);
      }



      ConfigInit 方法主要在初始化狀態管理器時通知其根據配置進行初始化工作,并將負責會話狀態清除的 SessionOnEndTarget 對象實例綁定到會話管理器(我們后面討論會話狀態管理實現時詳細討論)。對 OutOfProcStateClientManager 和 SqlStateClientManager 來說,在此階段還會初始化與外部服務器的連接,并通過一個 System.Web.Util.ResourcePool 實例,提供基于時間策略的資源池來維護連接;
      ResetTimeout 方法重置指定 Session 的超時時間;對 InProcStateClientManager 來說,這個超時時間是通過 System.Web.Caching.CacheInternal 類型實現的緩存對象來使用的; OutOfProcStateClientManager 直接通過 MakeRequest 函數構造請求發給外部獨立的狀態管理器執行; SqlStateClientManager 則調用存儲過程 TempResetTimeout 更新 ASPStateTempSessions 表的過期時間 Expires 字段;
      Dispose 方法是否狀態管理器的資源,落實到代碼就是對 OutOfProcStateClientManager 和 SqlStateClientManager 中資源池的釋放;

      Set 方法則將指定的 SessionStateItem 存儲到 id 相關的會話數據中,并根據 inStorage 指定的對象狀態,決定在發生異常的情況下是否釋放對此會話的鎖。與 ResetTimeout 的實現類似,OutOfProcStateClientManager 發送請求給外部獨立的狀態管理器;SqlStateClientManager 調用存儲過程 TempUpdateStateItemXXX 更新會話狀態表 ASPStateTempSessions 中的過期時間 Expires 字段、鎖定狀態 Lock 字段、以及狀態信息 SessionItemShort/SessionItemLong (分別保存7000字節以下或之上的數據)。如發生異常并設置 inStorage 標記,則先調用 TempReleaseStateItemExclusive 釋放會話鎖。

      對狀態管理器中數據的獲取較為復雜,IStateClientManager 接口使用的是異步調用的模式,并為提高效率將獨占的獲取數據單獨拿出來。狀態管理器實現類通過通用基類 System.Web.SessionState.StateClientManager 實現的幾個工具方法,將數據獲取操作異步化。再最終由實現類通過 Get 和 GetExclusive 方法完成操作。獲取數據的方法 InProcStateClientManager 通過緩存;OutOfProcStateClientManager 通過請求;SqlStateClientManager 通過 TempGetStateItemXXX 存儲過程完成。

      在了解了 SessionStateModule 控制的狀態服務器的實現和使用方法后,我們來看看上層的 HttpSessionState 是如何使用的。



      Mandeep S Bhatia 的 ASP.NET Session Management Internals 介紹了 HttpSessionState 內部完成狀態信息管理的原理。HttpSessionState 的 Item 屬性實際上是通過 SessionDictionary 實例實現的。
      以下內容為程序代碼:

      public sealed class HttpSessionState : ...
      {
      private SessionDictionary _dict;

      public object this[string name]
      {
      get
      {
      return _dict[name];
      }
      set
      {
      _dict[name] = value;
      }
      }
      }



      而此 SessionDictionary 實例與 HttpSessionState 實例的構造,都是在前面提到的完成會話構造的 SessionStateModule.CompleteAcquireState 方法中完成的:
      以下內容為程序代碼:

      public sealed class SessionStateModule : IHttpModule
      {
      private string _rqId;
      private SessionDictionary _rqDict;
      private HttpStaticObjectsCollection _rqStaticObjects; // 靜態對象,通過頁面中 <object Runat="Server" Scope="Session"/> 標記設置
      private int _rqTimeout;
      private bool _rqIsNewSession;
      private bool _rqReadonly;
      private HttpContext _rqContext;
      private SessionStateItem _rqItem;

      private void CompleteAcquireState()
      {
      if (_rqItem != null)
      {
      if (_rqItem.dict != null)
      {
      _rqDict = _rqItem.dict;
      }
      else
      {
      _rqDict = new SessionDictionary();
      }
      _rqStaticObjects = ((_rqItem.staticObjects != null) ? _rqItem.staticObjects :
      _rqContext.Application.SessionStaticObjects.Clone());
      _rqTimeout = _rqItem.timeout;
      _rqIsNewSession = false;
      _rqInStorage = true;
      _rqStreamLength = _rqItem.streamLength;
      }
      else
      {
      _rqDict = new SessionDictionary();
      _rqStaticObjects = _rqContext.Application.SessionStaticObjects.Clone();
      _rqTimeout = SessionStateModule.s_config._timeout;
      _rqIsNewSession = true;
      _rqInStorage = false;
      }
      _rqDict.Dirty = false;

      _rqSessionState = new HttpSessionState(_rqId, _rqDict, _rqStaticObjects, _rqTimeout, _rqIsNewSession,
      SessionStateModule.s_config._isCookieless, SessionStateModule.s_config._mode, _rqReadonly);

      _rqContext.Items.Add("AspSession", _rqSessionState);

      }
      }



      這兒涉及到的幾個字段,基本上都能跟 HttpSessionState 提供的公共屬性對應起來。需要注意的是 HttpSessionState.StaticObjects 是通過 ASP.NET 頁面上的 <object Runat="Server" Scope="Session"/> 類似標記靜態定義的;_rqReadonly 則是前面提到的 <%@ Page EnableSessionState=" ReadOnly" %> 標記設置的。

      至此,狀態管理器的使用與實現方法基本上分析完成,下面整理一下其使用流程:

      1.構造:HttpApplication 在初始化過程中調用 InitModules 初始化配置文件 Machine.config 中注冊的實現了 IHttpModule 接口的 HTTP 模塊;其中 SessionStateModule 作為模塊之一被構造并初始化;其 InitModuleFromConfig 方法根據配置文件中狀態管理器的相關配置,構造并初始化相應的狀態管理器;并根據各種條件調用 CompleteAcquireState 方法完成 HttpSessionState 的構造工作。
      2.使用:HttpSessionState 通過 SessionDictionary 實現其 Item 屬性的狀態數據管理;SessionDictionary 本身由 SessionStateModule.OnReleaseState 在適當的時候寫回狀態管理器;其他維護操作也是通過 SessionStateModule 調用狀態管理器的 IStateClientManager 接口完成的。
      3.實現:狀態管理器從抽象基類 StateClientManager 獲得異步調用的封裝;通過 IStateClientManager 接口提供給 SessionStateModule 管理其初始化、釋放和管理的接口。

      雖然 ASP.NET 做了很多工作,但個人感覺還遠遠不夠。例如 InProc/OutOfProc 實際上都是在內存中,只是解決了一個可靠性和數據集中同步的問題;SQL Server 雖然能夠解決容量、可靠性和數據集中同步的問題,但效率又受到影響。這方面 .NET 應該向 Java 好好學習一下,例如 Java 下 EHCache 和 OSCache 都提供了平滑的可配置二級(內存/硬盤)緩存介質切換,并且后者還提供了對負載均衡的簡單支持,此外還有 JBoss 等實現的基于 IP 多播等實現技術的負載均衡緩存實現等等,都遠遠超出了 ASP.NET 提供的緩存機制所考慮到的范圍。雖然 ASP.NET 也有獨立的緩存機制,MS 也提出了 Cache Application Block 的參考實現,不過還是任重而道


      日韩精品一区二区三区高清