[ASP.NET的二三事] Lesson 6 ASP.NET的狀態管理 Vol. 3 (State Management–Server-Side Statemanagement)

[ASP.NET的二三事] Lesson 6 ASP.NET的狀態管理 Vol. 3 (State Management–Server-Side Statemanagement)

前面說明了如何透過客戶端的電腦進行狀態管理

然而,並不是什麼樣的資料都適合在請求的時候都往客戶端送

必須考量了頻寬以及安全性的問題。

因此本次分享了三種透過Server端的儲存狀態資訊的機制

 

這兩種機制都不會將資料往客戶端送,以下以存取範圍后分三者:

1.Application是一種全域型的狀態儲存機制,可以被所有的使用者所存取

2.Session是一種由特定使用者方可存取的狀態儲存機制(User-Specific)

3.Profile機制也是一種由特定使用者可存取狀態的機制,類似於Session,只是儲存方式主要是在DB

 

1.Application

Application是ASP.NET中全域型(Global)的儲存機制,存在Application中的資料可以被所有應用程式中的Page所存取

Application的資料是儲存在HttpApplicationState類別的實體中,而且放在Page.Application的屬性裡(透過鍵值對的型態)

只是透過Application來儲存必須注意這樣的資訊是可以被所有應用程式中的Page所存取

因此要注意資料存取一致性的問題或是資料遺失的問題。

然而,資料儲存於Application的時候,並不是永久性的,因為它其實是將狀態儲存在伺服器的記憶體中

因此只要當Web伺服器(IIS)重新啟動,資料就會消失。因此要使用Application就要知道在應用程式中

何時應該儲存、讀取資料,以確保狀態會被適當的保存

 

為了瞭解上述的議題,就必須瞭解應用程式事件執行的始未

ASP.NET的應用程式生命週期(ASP.NET Application Life Cycle)

1.使用者初次請求了應用程式中的網頁時,會啟動應用程式生命週期起點。

2.該請求會被引導進入Process Pipeline(一種程序管道)

在IIS 5.0以及6.0以及7.0的Classic 模式下對於aspx的網頁,(還有ascx, .ashx and .asmx的請求)

都會透過ISAPI來執行。

 

(在IIS7.0的整合模式中會有一個通用的管道來處理所有應用程式內的資源請求

這使得資源(例:html檔案)可以用與aspx網頁相同的管道來傳送。

這可以讓Managed Code支援這些資源,例如管理存取資源的安全性)

 

3.ApplicationManager的類別實體被建立,這個實體是用來執行應用程式領域中對應用程式的請求

一個應用程式領域會將其他應用程式的全域變數隔離開來,以免互相被存取到

 

4.當應用程式領域被建立以後,HostingEnviroment的類別實體會被建立,這個實體的功能是為了讓應用程式

可以處理資料夾、目錄資源。

 

5.下一步ASP.NET會建立處理請求的核心物件,包含了HttpContextHttpRequest、以及HttpResponse

6.Application開始啟動的時候,HttpApplication類別會被建立(或是重覆使用已建立實體),這個類別會被用來執行Global.asax的檔案

這個檔案是用來定義應用程式啟動與中止事件的檔案。(註:當HttpAppliction被建立的時候,SessionStateModule也會被建立。)

 

7.最終,請求都是透過HttpApplication的管道(Pipeline)來處理,這個管道包含了驗證請求、對應站台的URL以及存取應用程式的快取。

 

Application可以透過下列的事件(定義在Global.asax)來定義Application中生命週期各階段的對應事件:

1.Application_Start

這個事件通常是因為初次請求應用程式頁面時,被IIS所觸發。

在這個事件中可以初始化Application層級的變數

2.Application_End

這個事件通常都是在應用程式被關閉(Shutdown),通常可以釋放資源,或是記錄特定事件(Log)

3.Application_Error

這個事件是在非預期的例外事件產生,並被拋到應用程式層級,我們可以在這裡進行應用程式最壞狀況的處理,例如進行Log。

4.Application_LogRequest

這個機制是當應用程式接到請求的時候,都會被觸發。

5.Application_PostLogRequest

當完成請求後會被觸發。

其他事件請參考MSDN中對於HttpApplication類別的事件。(例如.. BeginRequest、EndRequest)

 

當瞭解了Application的生命週期後,我們可以適度的在生命週期的期間進行特定的行為

例如初始化以及Log機制。

 

先前提到Application是一種鍵值對的存取形式

但是正因為它是全域型的存取範圍,因此有可能會被許多頁面同時請求這份快取資訊。

其中若有寫入的情況,就會有可能會發生問題,因此在寫入Application的時候

記得要在Application.Lock();以及Application.UnLock()的程式區間進行。

假如不這樣做,就有可能導致當一個頁面在儲存Application的資料時

另一頁同時要儲存而造成資料的遺失(或稱為覆寫)。

image

來自於MSDN

 

註:進一步ASP.NET有另一個類別,可以讓我們使用Cache來存取,Cache的機制具備比Application更多的彈性

較大的差異則包含了,Cache的資料會適度進行資源的釋放,另外則包含了CallBack機制

相關訊息請看:

http://msdn.microsoft.com/en-us/library/6hbbsfk6(v=vs.71).aspx

 

2.Session

多數的Web應用程式都需要有User-Specific的狀態儲存

Session與Application狀態儲存上的最大差異點就是SessionState是區別瀏覽器(或為一個User)的存取範圍。

而且僅供特定的Session期間所存取,而不是整個站台共用。在Session期間,可以跨不同頁面存取

不過在Session結束(或是過期),資料就會不見了

因此我們還要進行一些動作,才能永久保存資料(例如存在DB)

 

Session提供了數個模式,來保存資料,預設是僅儲存在伺服器的記憶體中

此外還提供透過Cookie或是其他StateServer,甚至是SQL Server來儲存。

因此假如有多個Front-End的伺服器架構需求,我們可以考量其他中央儲存的機制。

 

Session是透過了鍵值對的方式來存取狀態,儲存在Session的物件中(HttpSessionState的類別)

強調一點是Session僅能儲存可序列化的資料(Serializable)

語法範例:

image

來自於MSDN

 

關於Session的設定

1.關閉SessionState

在Web.config的Config檔案中,可以直接將整個站台的Session關掉

<configuration>

     <system.web>

          <sessionState mode="off" />

     </system.web>

</configuration>

或是透過直接在aspx的頁面中的頁頭加入EnableSessionState="False"來設定特定頁面是否要啟動Session

2.設定Cookieless的Session

Web.config的Config檔案中,可以設定

<configuration>

     <system.web>

          <sessionState cookieless="true" regenerateExpiredSessionId="true"

            />

     </system.web>

</configuration>

預設的情況下,Session狀態是透過使用者的Cookie來追縱使用者SessionID(如前面Lesson所示)

這也是最佳的方式,但使用者仍可能關閉Cookie,所以ASP.NET可以讓我們設定Cookieless

這個時候,Session的ID會綁入頁面請求的URL中,並綁在虛擬目錄前,以目錄結構來傳遞:

例:http://www.pin0513.com/s(lit3py55t21z4v44vlm25s55)/detail.aspx(假的)

其中lit3py55t21z4v44vlm25s55就是SessionID

3.反應Session的事件

Session事件可以像Application,在Global.asax的檔案中撰寫對應的事件,例如Session_Start以及Session_End

這時我們可以在其中撰寫對應的事件處理,例如從資料庫中讀取資訊或是回存資訊。

註:Session_End僅適用於InProc的模式(下面會介紹),若採用其他模式儲存Session,則不會引發本事件

4.Session狀態模式

因應Session預設是InProc模式,也就是將狀態儲存於記憶體中,因此可能就會有擴展性(Scalability)的問題了

如同先前說的,我們的架構中可能會有Load-Balancer導向多個伺服器或是VM來處理許多客戶的請求

這個時候,若僅將狀態儲存在某台的記憶體中,會造成接下來的請求可能由不同伺服器來處理時

造成遺失狀態的情況。因此ASP.NET提供了許多方法來讓Session的狀態得以儲存:

1.InProc:單一站台下的最佳選擇,也是預設的模式,但若有Load Balancer的情境,請考量其他方法

2.StateServer:StateServer其實就是另一台電腦專門來快取並提供所有應用程式狀態的服務

即使在應用程式伺服器掛點的話,StateServer仍可以保持狀態的資訊。

只是必須要在應用程式中,手動將請求State Service設定為Automatic。

3.SQLServer:透過資料庫來儲存狀態資訊,這也是透過中央儲存狀態的方式,

因此也適用於多伺服器的架構,但是透過SQL Server就會有效能上的瓶頸點

因此最好進行效能的測試。

4.Custom:我們也可以自訂狀態來源提供者,只是我們要自行實作程式

上述模式都需要在Web.config中進行設定,就可以套用到整個應用程式站台了。

 

Profile

Profile是ASP.NET提供另一個User-Specific的資料儲存機制,比Session不同的地方是它預設就是儲存在DB

若要使用這個機制的話,必須設定SqlProfile Provider的類別,當然也可以實作自行的做法。

像是XML或是WebService。透過此方法也適用於伺服器架構下(WebFarm)資料的儲存方式。

 

老話一句,在架構的選擇上,若將狀態存取切成多層的話(例如StateServer以及DB)

那也可能會有效能的問題會發生,應考量應用程式中其存取範圍以及後續的擴展性的需求

此外,狀態管理這個議題會持續下去,技術也會不斷的推出,讓我們開發者來更方便的進行開發

MS也有很多新的技術來支持WebFarm的架構,例如AppFabric或是Cache機制

都是希望能夠在雲端的架構需求下取得更佳的彈性與效能。

 

參考資料:

書籍的資料來源:MCTS for ASP.NET