Concurrent Requests for Writeable Session Variables

在之前有讨论到不同网页使用到 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,如下,


  
	
  

有需要的朋友可以试看看哦! 特别是那种维护旧系统的朋友,如果改了 ReadOnly 又怕会影响到什么功能,就可以用 方法1 哦!

参考数据

When a Single ASP.NET Client makes Concurrent Requests for Writeable Session Variables

设定 WebService 使用 ReadOnly Session

使用Session要小心网页会被 卡住 哦!