在之前有討論到不同網頁使用到 Writeable Session 時會卡來卡去的狀況 Lock or Blocking(使用Session要小心網頁會被 卡住 哦!),
或是 Web Service 如果開啟 Session 存取的話,預設是 Writeable Session 模式(設定 WebService 使用 ReadOnly Session)。
解法除了設定 SessionStateBehavior.ReadOnly 外,
When a Single ASP.NET Client makes Concurrent Requests for Writeable Session Variables 這篇還提供2個方式,
1.降低 Session Lock Check 的時間
2.實作 Lockless SessionStateStoreProvider
維護舊系統的朋友,如果改了 ReadOnly 又怕會影響到什麼功能,可以參考看看哦!
在寫入 Session 時會造成 500ms 的 Delay 時間(Storing Anything in ASP.NET Session Causes 500ms Delays),
如果一定會寫到 Writeable Session 時,有以下2個方式,
1.降低 Session Lock Check 的時間
可以在 Global.asax.cs 的 Application_Start Method 去設定預設值,如下,
protected void Application_Start(object sender, EventArgs e)
{
var sessionStateModuleType = typeof(SessionStateModule);
var pollingIntervalFieldInfo = sessionStateModuleType.GetField("LOCKED_ITEM_POLLING_INTERVAL", BindingFlags.NonPublic | BindingFlags.Static);
var orgpollingInterval = pollingIntervalFieldInfo.GetValue(null);
pollingIntervalFieldInfo.SetValue(null, 30); // default 500ms
var pollingDeltaFieldInfo = sessionStateModuleType.GetField("LOCKED_ITEM_POLLING_DELTA", BindingFlags.NonPublic | BindingFlags.Static);
var orgPollingDelta = pollingDeltaFieldInfo.GetValue(null);
pollingDeltaFieldInfo.SetValue(null, TimeSpan.FromMilliseconds(15.0)); // default 250ms
}
所以每次 request 時,可以發現 Polling Interval 從 500ms 改成 30ms, Polling Delta 從 250ms 改成 15ms,如下,
2.實作 Lockless SessionStateStoreProvider
public class LocklessInProcSessionStateStore : SessionStateStoreProviderBase
{
private SessionStateStoreProviderBase _store;
public override void Initialize(string name, NameValueCollection config)
{
base.Initialize(name, config);
var storeType = typeof(SessionStateStoreProviderBase).Assembly.GetType("System.Web.SessionState.InProcSessionStateStore");
_store = (SessionStateStoreProviderBase)Activator.CreateInstance(storeType);
_store.Initialize(name, config);
}
public override void Dispose()
{
_store.Dispose();
}
public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback)
{
return _store.SetItemExpireCallback(expireCallback);
}
public override void InitializeRequest(HttpContext context)
{
_store.InitializeRequest(context);
}
public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId,
out SessionStateActions actions)
{
var returnValue = _store.GetItem(context, id, out locked, out lockAge, out lockId, out actions);
if (returnValue == null && lockId != null)
{
_store.ReleaseItemExclusive(context, id, lockId);
returnValue = _store.GetItem(context, id, out locked, out lockAge, out lockId, out actions);
}
return returnValue;
}
public override SessionStateStoreData GetItemExclusive(HttpContext context, string id, out bool locked, out TimeSpan lockAge,
out object lockId, out SessionStateActions actions)
{
var returnValue = _store.GetItemExclusive(context, id, out locked, out lockAge, out lockId, out actions);
if (returnValue == null && lockId != null)
{
_store.ReleaseItemExclusive(context, id, lockId);
returnValue = _store.GetItemExclusive(context, id, out locked, out lockAge, out lockId, out actions);
}
return returnValue;
}
public override void ReleaseItemExclusive(HttpContext context, string id, object lockId)
{
_store.ReleaseItemExclusive(context, id, lockId);
}
public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem)
{
_store.SetAndReleaseItemExclusive(context, id, item, lockId, newItem);
}
public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item)
{
_store.RemoveItem(context, id, lockId, item);
}
public override void ResetItemTimeout(HttpContext context, string id)
{
_store.ResetItemTimeout(context, id);
}
public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout)
{
return _store.CreateNewStoreData(context, timeout);
}
public override void CreateUninitializedItem(HttpContext context, string id, int timeout)
{
_store.CreateUninitializedItem(context, id, timeout);
}
public override void EndRequest(HttpContext context)
{
_store.EndRequest(context);
}
}
在 web.config 中加入設定自定的 SessionState,如下,
<sessionState mode="Custom" customProvider="LocklessInProcSessionStateStore" cookieless="false" timeout="1" >
<providers>
<add name="LocklessInProcSessionStateStore"
type="你專案的namespace.LocklessInProcSessionStateStore"/>
</providers>
</sessionState>
有需要的朋友可以試看看哦! 特別是那種維護舊系統的朋友,如果改了 ReadOnly 又怕會影響到什麼功能,就可以用 方法1 哦!
參考資料
When a Single ASP.NET Client makes Concurrent Requests for Writeable Session Variables
設定 WebService 使用 ReadOnly Session
Hi,
亂馬客Blog已移到了 「亂馬客 : Re:從零開始的軟體開發生活」
請大家繼續支持 ^_^