觀察ASP.NET State Server的資料傳輸時機
5 |
ASP.NET的Session有四種Session State Mode: InProc、StateServer、SQLServer、Custom。當使用StateServer或SQLServer作為儲存處所,由於與Web Server分屬不同Process,甚至位於不同主機,故ASP.NET Web Application必須將存於Session的物件序列化,再透過網路傳送到StateServer或SQL Server保存;讀取Session資料時,則反過來先透過網路由StateServer或SQL Server接收資料,再反序列化還原成當初寫入的物件供ASP.NET程式存取。
ASP.NET Web Application與StateServer間傳送或接收資料的時機,有兩種可能的做法: 1) 每次讀取或寫入Session[“..."]就立即進行傳輸,或是 2) 讀入後先Cache在記憶體供更新操作,稍後再批次寫回。感覺上第1)種做法會產生過多往返(Round-Trip),不利於效能(例如: 用for迴圈更新1000次Session變數的情境),ASP.NET採用第2)種方式的可能性較高。但爬文查詢卻沒找到運作原理的相關說明足供佐證,但事關對ASP.NET端Session物件行為的判斷,決定做個實驗來驗證:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
public partial class TestStateServer : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
object notThere = Session["NotThere"];
Thread.Sleep(1000);
Session["C"] = 1;
Thread.Sleep(1000);
for (int i = 0; i < 10; i++)
{
Session["C"] = (int)Session["C"] + 1;
Thread.Sleep(500);
}
Response.Write((int)Session["C"]);
Response.End();
}
}
程式先讀取一個不存在的Session[“NotThere”],1秒後設定Session[“C”]=1,再等1秒開始每隔0.5秒做一次Session[“C”] = (int)Session[“C”] + 1,連續進行10次,最後顯示Session["C"]。ASP.NET程式在172.28.1.1執行,修改web.config使用另一台機器(172.28.1.101)上的ASP.NET State Server存放Session[設定方法可參考保哥的文章],測試時使用Microsoft Network Monitor擷取封包,觀察兩台主機間的網路傳輸。
如果ASP.NET使用的是上述2)的批次同步做法,最精省的情況只需要兩次傳輸,Request一開始時就讀取並將全部Session資料放進記憶體,後續更新均直接作用在記憶體上,Response.End()後再一次寫回StateServer;若是採1)即寫即傳的做法,理論上會看到11次以上的傳輸。
以上是重新啟動IIS後執行一次ASP.NET網頁所觀察到的結果,分析推測如下:
Frame 6: Web向StateServer發出GET請求,試圖以SeesionID取回Session資料
(推測是在Session_Start事件時期發出。經過對照測試,即便未用到Session也會發生)
Frame 7: StateServer回應404 Not Found (為新Session,不存在任何資料)
Frame 8: Web送出PUT,寫入空白Session資料
Frame 9: StateServer回應200 OK
Frame 10: Web送出GET (應為網頁一開始執行時觸發)
Frame 11: StateServer傳回剛才PUT的內容
Frame 13: Web送出PUT,內容中包含”C”及16進位數字0x0b == 11
(Frame 13與11相隔剛好7秒 = 1 + 1 + 0.5 * 10,判斷是Response.End後才寫入)
Frame 14: StateServer回應200 OK
【結論】
由實驗觀察得知,ASP.NET Web Application與StateServer間採用批次傳輸,Response開始時由StateServer讀取Session資料並快取在記憶體、ASP.NET WebPage執行完畢後將最後的Session結果寫回StateServer。
Comments
# by 阿修
你好,有個關於Session的問題想請教 因為物件須序列化才能傳到Server上, 但今天我預到ㄧ個錯誤是 system.web.mvchtmlstring 無法被序列化。 那我的疑問是, 這個原生的Class是否有使用到session機制呢? 導致出現這個錯誤,以及我有辦法避掉這問題嗎?
# by Jeffrey
to 阿修,物件需要序列化後才能跨機器傳輸,這是本質上的限制,並非所有原生型別都支援序列化,我建議重新考量你要存入Session的內容,最好只擷取資料部分儲存,不要將整顆物件存入,一方面避免無法序列化問題,一方面也提升儲存空間及傳輸效率。
# by 阿修
Jeffery, 謝謝你,有另一個問題想請問 有些物件看似不是我主動存在Session中的,我該怎麼去排除他?
# by Jeffrey
to 阿修,意思是你想避免某段不是你寫的程式碼將一個不能序列化的物件塞入Session導致錯誤?除了去追查存入Session的程式來源及理由,試著改變它的行為,我沒想到更好的方法。
# by 阿修
感謝你,問題已解決,後來追查有找到原因