最近项目用到了RedisSessionStateProvider来保存session,发现比内存session慢,后来慢慢了解,发现asp.net session是有锁的。我在文章 你的项目真的需要Session吗? redis保存session性能怎么样?也提到一些观点,本来打算在那篇文章补充一些类容,后来想了一下,还是重写一个短文吧。有关session 管道流程大家 可以参考 Asp.net Session认识加强-Session究竟是如何存储你知道吗?

我们的mvc程序都是有路由信息,那么就离不开UrlRoutingModule 该code如下:

  1. namespace System.Web.Routing {
  2. using System.Diagnostics;
  3. using System.Globalization;
  4. using System.Runtime.CompilerServices;
  5. using System.Web.Security;
  6.  
  7. [TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
  8. public class UrlRoutingModule : IHttpModule {
  9. private static readonly object _contextKey = new Object();
  10. private static readonly object _requestDataKey = new Object();
  11. private RouteCollection _routeCollection;
  12.  
  13. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
  14. Justification = "This needs to be settable for unit tests.")]
  15. public RouteCollection RouteCollection {
  16. get {
  17. if (_routeCollection == null) {
  18. _routeCollection = RouteTable.Routes;
  19. }
  20. return _routeCollection;
  21. }
  22. set {
  23. _routeCollection = value;
  24. }
  25. }
  26.  
  27. protected virtual void Dispose() {
  28. }
  29.  
  30. protected virtual void Init(HttpApplication application) {
  31.  
  32. //////////////////////////////////////////////////////////////////
  33. // Check if this module has been already addded
  34. if (application.Context.Items[_contextKey] != null) {
  35. return; // already added to the pipeline
  36. }
  37. application.Context.Items[_contextKey] = _contextKey;
  38.  
  39. // Ideally we would use the MapRequestHandler event. However, MapRequestHandler is not available
  40. // in II6 or IIS7 ISAPI Mode. Instead, we use PostResolveRequestCache, which is the event immediately
  41. // before MapRequestHandler. This allows use to use one common codepath for all versions of IIS.
  42. application.PostResolveRequestCache += OnApplicationPostResolveRequestCache;
  43. }
  44.  
  45. private void OnApplicationPostResolveRequestCache(object sender, EventArgs e) {
  46. HttpApplication app = (HttpApplication)sender;
  47. HttpContextBase context = new HttpContextWrapper(app.Context);
  48. PostResolveRequestCache(context);
  49. }
  50.  
  51. [Obsolete("This method is obsolete. Override the Init method to use the PostMapRequestHandler event.")]
  52. public virtual void PostMapRequestHandler(HttpContextBase context) {
  53. // Backwards compat with 3.5 which used to have code here to Rewrite the URL
  54. }
  55.  
  56. public virtual void PostResolveRequestCache(HttpContextBase context) {
  57. // Match the incoming URL against the route table
  58. RouteData routeData = RouteCollection.GetRouteData(context);
  59.  
  60. // Do nothing if no route found
  61. if (routeData == null) {
  62. return;
  63. }
  64.  
  65. // If a route was found, get an IHttpHandler from the route's RouteHandler
  66. IRouteHandler routeHandler = routeData.RouteHandler;
  67. if (routeHandler == null) {
  68. throw new InvalidOperationException(
  69. String.Format(
  70. CultureInfo.CurrentCulture,
  71. SR.GetString(SR.UrlRoutingModule_NoRouteHandler)));
  72. }
  73.  
  74. // This is a special IRouteHandler that tells the routing module to stop processing
  75. // routes and to let the fallback handler handle the request.
  76. if (routeHandler is StopRoutingHandler) {
  77. return;
  78. }
  79.  
  80. RequestContext requestContext = new RequestContext(context, routeData);
  81.  
  82. // Dev10 766875 Adding RouteData to HttpContext
  83. context.Request.RequestContext = requestContext;
  84.  
  85. IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
  86. if (httpHandler == null) {
  87. throw new InvalidOperationException(
  88. String.Format(
  89. CultureInfo.CurrentUICulture,
  90. SR.GetString(SR.UrlRoutingModule_NoHttpHandler),
  91. routeHandler.GetType()));
  92. }
  93.  
  94. if (httpHandler is UrlAuthFailureHandler) {
  95. if (FormsAuthenticationModule.FormsAuthRequired) {
  96. UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
  97. return;
  98. }
  99. else {
  100. throw new HttpException(, SR.GetString(SR.Assess_Denied_Description3));
  101. }
  102. }
  103.  
  104. // Remap IIS7 to our handler
  105. context.RemapHandler(httpHandler);
  106. }
  107.  
  108. #region IHttpModule Members
  109. void IHttpModule.Dispose() {
  110. Dispose();
  111. }
  112.  
  113. void IHttpModule.Init(HttpApplication application) {
  114. Init(application);
  115. }
  116. #endregion
  117. }
  118. }

在PostResolveRequestCache方法中   IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext); 这么一句。这里的routeHandler其实默认是MvcRouteHandler,所以智力其实是调用MvcRouteHandler的GetHttpHandler方法。

MvcRouteHandler的code:

  1. // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
  2.  
  3. using System.Web.Mvc.Properties;
  4. using System.Web.Routing;
  5. using System.Web.SessionState;
  6.  
  7. namespace System.Web.Mvc
  8. {
  9. public class MvcRouteHandler : IRouteHandler
  10. {
  11. private IControllerFactory _controllerFactory;
  12.  
  13. public MvcRouteHandler()
  14. {
  15. }
  16.  
  17. public MvcRouteHandler(IControllerFactory controllerFactory)
  18. {
  19. _controllerFactory = controllerFactory;
  20. }
  21.  
  22. protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
  23. {
  24. requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));
  25. return new MvcHandler(requestContext);
  26. }
  27.  
  28. protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext)
  29. {
  30. string controllerName = (string)requestContext.RouteData.Values["controller"];
  31. if (String.IsNullOrWhiteSpace(controllerName))
  32. {
  33. throw new InvalidOperationException(MvcResources.MvcRouteHandler_RouteValuesHasNoController);
  34. }
  35.  
  36. IControllerFactory controllerFactory = _controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();
  37. return controllerFactory.GetControllerSessionBehavior(requestContext, controllerName);
  38. }
  39.  
  40. #region IRouteHandler Members
  41.  
  42. IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
  43. {
  44. return GetHttpHandler(requestContext);
  45. }
  46.  
  47. #endregion
  48. }
  49. }

在MvcRouteHandler中GetHttpHandler设置SessionStateBehavior:

protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
{
requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));
return new MvcHandler(requestContext);
}

SessionStateBehavior的值默认来源于DefaultControllerFactory的GetControllerSessionBehavior方法,有SessionStateAttribute特性就取其值,否者默认的SessionStateBehavior.Default

  1. SessionStateBehavior IControllerFactory.GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
  2. {
  3. if (requestContext == null)
  4. {
  5. throw new ArgumentNullException("requestContext");
  6. }
  7. if (String.IsNullOrEmpty(controllerName))
  8. {
  9. throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
  10. }
  11.  
  12. Type controllerType = GetControllerType(requestContext, controllerName);
  13. return GetControllerSessionBehavior(requestContext, controllerType);
  14. }
  15.  
  16. protected internal virtual SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
  17. {
  18. if (controllerType == null)
  19. {
  20. return SessionStateBehavior.Default;
  21. }
  22.  
  23. return _sessionStateCache.GetOrAdd(
  24. controllerType,
  25. type =>
  26. {
  27. var attr = type.GetCustomAttributes(typeof(SessionStateAttribute), inherit: true)
  28. .OfType<SessionStateAttribute>()
  29. .FirstOrDefault();
  30.  
  31. return (attr != null) ? attr.Behavior : SessionStateBehavior.Default;
  32. });
  33. }

那么HttpContext.SetSessionStateBehavior方法又是如何实现的:

  1. internal SessionStateBehavior SessionStateBehavior { get; set; }
  2.  
  3. [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate",
  4. Justification = "An internal property already exists. This method does additional work.")]
  5. public void SetSessionStateBehavior(SessionStateBehavior sessionStateBehavior) {
  6. if (_notificationContext != null && _notificationContext.CurrentNotification >= RequestNotification.AcquireRequestState) {
  7. throw new InvalidOperationException(SR.GetString(SR.Invoke_before_pipeline_event, "HttpContext.SetSessionStateBehavior", "HttpApplication.AcquireRequestState"));
  8. }
  9.  
  10. SessionStateBehavior = sessionStateBehavior;
  11. }

其实很简单,就是设置了一个属性,这里还有一个ReadOnlySessionState属性很重要,他需要读取SessionStateBehavior属性。由于MvcHandler 默认继承了IRequiresSessionState接口但是没有继承IReadOnlySessionState,

所以默认RequiresSessionState为true,ReadOnlySessionState为false。

  1. public IHttpHandler Handler {
  2. get { return _handler;}
  3. set {
  4. _handler = value;
  5. _requiresSessionStateFromHandler = false;
  6. _readOnlySessionStateFromHandler = false;
  7. InAspCompatMode = false;
  8. if (_handler != null) {
  9. if (_handler is IRequiresSessionState) {
  10. _requiresSessionStateFromHandler = true;
  11. }
  12. if (_handler is IReadOnlySessionState) {
  13. _readOnlySessionStateFromHandler = true;
  14. }
  15. Page page = _handler as Page;
  16. if (page != null && page.IsInAspCompatMode) {
  17. InAspCompatMode = true;
  18. }
  19. }
  20. }
  21. }
  22.  
  23. // session state support
  24. private bool _requiresSessionStateFromHandler;
  25. internal bool RequiresSessionState {
  26. get {
  27. switch (SessionStateBehavior) {
  28. case SessionStateBehavior.Required:
  29. case SessionStateBehavior.ReadOnly:
  30. return true;
  31. case SessionStateBehavior.Disabled:
  32. return false;
  33. case SessionStateBehavior.Default:
  34. default:
  35. return _requiresSessionStateFromHandler;
  36. }
  37. }
  38. }
  39.  
  40. private bool _readOnlySessionStateFromHandler;
  41. internal bool ReadOnlySessionState {
  42. get {
  43. switch (SessionStateBehavior) {
  44. case SessionStateBehavior.ReadOnly:
  45. return true;
  46. case SessionStateBehavior.Required:
  47. case SessionStateBehavior.Disabled:
  48. return false;
  49. case SessionStateBehavior.Default:
  50. default:
  51. return _readOnlySessionStateFromHandler;
  52. }
  53. }
  54. }

在SessionStateModule的GetSessionStateItem方法里面有如下code:

这里我们用的是RedisSessionStateProvider,其code如下:

  1. //
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. // Licensed under the MIT License. See License.txt in the project root for license information.
  4. //
  5.  
  6. using System;
  7. using System.Web;
  8. using System.Web.SessionState;
  9.  
  10. namespace Microsoft.Web.Redis
  11. {
  12. public class RedisSessionStateProvider : SessionStateStoreProviderBase
  13. {
  14. // We want to release lock (if exists) during EndRequest, to do that we need session-id and lockId but EndRequest do not have these parameter passed to it.
  15. // So we are going to store 'sessionId' and 'lockId' when we acquire lock. so that EndRequest can release lock at the end.
  16. // If we removed the lock before that than we will clear these by our self so that EndRequest won't do that again (only Release item exclusive does that).
  17. internal string sessionId;
  18. internal object sessionLockId;
  19. private const int FROM_MIN_TO_SEC = ;
  20.  
  21. internal static ProviderConfiguration configuration;
  22. internal static object configurationCreationLock = new object();
  23. internal ICacheConnection cache;
  24.  
  25. private static object _lastException = new object();
  26.  
  27. /// <summary>
  28. /// We do not want to throw exception from session state provider because this will break customer application and they can't get chance to handel it.
  29. /// So if exception occurs because of some problem we store it in HttpContext using a key that we know and return null to customer. Now, when customer
  30. /// get null from any of session operation they should call this method to identify if there was any exception and because of that got null.
  31. /// </summary>
  32. public static Exception LastException
  33. {
  34. get
  35. {
  36. if (HttpContext.Current != null)
  37. {
  38. return (Exception) HttpContext.Current.Items[_lastException];
  39. }
  40. return null;
  41. }
  42.  
  43. set
  44. {
  45. if (HttpContext.Current != null)
  46. {
  47. HttpContext.Current.Items[_lastException] = value;
  48. }
  49. }
  50. }
  51.  
  52. private void GetAccessToStore(string id)
  53. {
  54. if (cache == null)
  55. {
  56. cache = new RedisConnectionWrapper(configuration, id);
  57. }
  58. else
  59. {
  60. cache.Keys.RegenerateKeyStringIfIdModified(id, configuration.ApplicationName);
  61. }
  62. }
  63.  
  64. public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
  65. {
  66. if (config == null)
  67. {
  68. throw new ArgumentNullException("config");
  69. }
  70.  
  71. if (name == null || name.Length == )
  72. {
  73. name = "MyCacheStore";
  74. }
  75.  
  76. if (String.IsNullOrEmpty(config["description"]))
  77. {
  78. config.Remove("description");
  79. config.Add("description", "Redis as a session data store");
  80. }
  81.  
  82. base.Initialize(name, config);
  83.  
  84. // If configuration exists then use it otherwise read from config file and create one
  85. if (configuration == null)
  86. {
  87. lock (configurationCreationLock)
  88. {
  89. if (configuration == null)
  90. {
  91. configuration = ProviderConfiguration.ProviderConfigurationForSessionState(config);
  92. }
  93. }
  94. }
  95. }
  96.  
  97. public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback)
  98. {
  99. //We don't receive notifications when cache items expire, so we can't support Session_OnEnd.
  100. return false;
  101. }
  102.  
  103. public override void InitializeRequest(HttpContext context)
  104. {
  105. //Not need. Initializing in 'Initialize method'.
  106. }
  107.  
  108. public override void Dispose()
  109. {
  110. //Not needed. Cleanup is done in 'EndRequest'.
  111. }
  112.  
  113. public override void EndRequest(HttpContext context)
  114. {
  115. try
  116. {
  117. // This check is required for unit tests to work
  118. int sessionTimeoutInSeconds;
  119. if (context != null && context.Session != null)
  120. {
  121. sessionTimeoutInSeconds = context.Session.Timeout * FROM_MIN_TO_SEC;
  122. }
  123. else
  124. {
  125. sessionTimeoutInSeconds = (int)configuration.SessionTimeout.TotalSeconds;
  126. }
  127.  
  128. if (sessionId != null && sessionLockId != null)
  129. {
  130. GetAccessToStore(sessionId);
  131. cache.TryReleaseLockIfLockIdMatch(sessionLockId, sessionTimeoutInSeconds);
  132. LogUtility.LogInfo("EndRequest => Session Id: {0}, Session provider object: {1} => Lock Released with lockId {2}.", sessionId, this.GetHashCode(), sessionLockId);
  133. sessionId = null;
  134. sessionLockId = null;
  135. }
  136. cache = null;
  137. }
  138. catch (Exception e)
  139. {
  140. LogUtility.LogError("EndRequest => {0}", e.ToString());
  141. LastException = e;
  142. if (configuration.ThrowOnError)
  143. {
  144. throw;
  145. }
  146. }
  147. }
  148.  
  149. public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout)
  150. {
  151. //Creating empty session store data and return it.
  152. LogUtility.LogInfo("CreateNewStoreData => Session provider object: {0}.", this.GetHashCode());
  153. return new SessionStateStoreData(new ChangeTrackingSessionStateItemCollection(), new HttpStaticObjectsCollection(), timeout);
  154. }
  155.  
  156. public override void CreateUninitializedItem(HttpContext context, string id, int timeout)
  157. {
  158. try
  159. {
  160. if (LastException == null)
  161. {
  162. LogUtility.LogInfo("CreateUninitializedItem => Session Id: {0}, Session provider object: {1}.", id, this.GetHashCode());
  163. ISessionStateItemCollection sessionData = new ChangeTrackingSessionStateItemCollection();
  164. sessionData["SessionStateActions"] = SessionStateActions.InitializeItem;
  165. GetAccessToStore(id);
  166. // Converting timout from min to sec
  167. cache.Set(sessionData, (timeout * FROM_MIN_TO_SEC));
  168. }
  169. }
  170. catch (Exception e)
  171. {
  172. LogUtility.LogError("CreateUninitializedItem => {0}", e.ToString());
  173. LastException = e;
  174. if (configuration.ThrowOnError)
  175. {
  176. throw;
  177. }
  178. }
  179. }
  180.  
  181. public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
  182. {
  183. LogUtility.LogInfo("GetItem => Session Id: {0}, Session provider object: {1}.", id, this.GetHashCode());
  184. return GetItemFromSessionStore(false, context, id, out locked, out lockAge, out lockId, out actions);
  185. }
  186.  
  187. public override SessionStateStoreData GetItemExclusive(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
  188. {
  189. LogUtility.LogInfo("GetItemExclusive => Session Id: {0}, Session provider object: {1}.", id, this.GetHashCode());
  190. return GetItemFromSessionStore(true, context, id, out locked, out lockAge, out lockId, out actions);
  191. }
  192.  
  193. private SessionStateStoreData GetItemFromSessionStore(bool isWriteLockRequired, HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
  194. {
  195. try
  196. {
  197. SessionStateStoreData sessionStateStoreData = null;
  198. locked = false;
  199. lockAge = TimeSpan.Zero;
  200. lockId = ;
  201. actions = SessionStateActions.None;
  202. if (id == null)
  203. {
  204. return null;
  205. }
  206. GetAccessToStore(id);
  207. ISessionStateItemCollection sessionData = null;
  208.  
  209. int sessionTimeout;
  210. bool isLockTaken = false;
  211. //Take read or write lock and if locking successful than get data in sessionData and also update session timeout
  212. if (isWriteLockRequired)
  213. {
  214. isLockTaken = cache.TryTakeWriteLockAndGetData(DateTime.Now, (int)configuration.RequestTimeout.TotalSeconds, out lockId, out sessionData, out sessionTimeout);
  215. sessionId = id; // signal that we have to remove lock in EndRequest
  216. sessionLockId = lockId; // save lockId for EndRequest
  217. }
  218. else
  219. {
  220. isLockTaken = cache.TryCheckWriteLockAndGetData(out lockId, out sessionData, out sessionTimeout);
  221. }
  222.  
  223. if (isLockTaken)
  224. {
  225. locked = false;
  226. LogUtility.LogInfo("GetItemFromSessionStore => Session Id: {0}, Session provider object: {1} => Lock taken with lockId: {2}", id, this.GetHashCode(), lockId);
  227. }
  228. else
  229. {
  230. sessionId = null;
  231. sessionLockId = null;
  232. locked = true;
  233. LogUtility.LogInfo("GetItemFromSessionStore => Session Id: {0}, Session provider object: {1} => Can not lock, Someone else has lock and lockId is {2}", id, this.GetHashCode(), lockId);
  234. }
  235.  
  236. // If locking is not successful then do not return any result just return lockAge, locked=true and lockId.
  237. // ASP.NET tries to acquire lock again in 0.5 sec by calling this method again. Using lockAge it finds if
  238. // lock has been taken more than http request timeout than ASP.NET calls ReleaseItemExclusive and calls this method again to get lock.
  239. if (locked)
  240. {
  241. lockAge = cache.GetLockAge(lockId);
  242. return null;
  243. }
  244.  
  245. if (sessionData == null)
  246. {
  247. // If session data do not exists means it might be exipred and removed. So return null so that asp.net can call CreateUninitializedItem and start again.
  248. // But we just locked the record so first release it
  249. ReleaseItemExclusive(context, id, lockId);
  250. return null;
  251. }
  252.  
  253. // Restore action flag from session data
  254. if (sessionData["SessionStateActions"] != null)
  255. {
  256. actions = (SessionStateActions)Enum.Parse(typeof(SessionStateActions), sessionData["SessionStateActions"].ToString());
  257. }
  258.  
  259. //Get data related to this session from sessionDataDictionary and populate session items
  260. sessionData.Dirty = false;
  261. sessionStateStoreData = new SessionStateStoreData(sessionData, new HttpStaticObjectsCollection(), sessionTimeout);
  262. return sessionStateStoreData;
  263. }
  264. catch (Exception e)
  265. {
  266. LogUtility.LogError("GetItemFromSessionStore => {0}", e.ToString());
  267. locked = false;
  268. lockId = null;
  269. lockAge = TimeSpan.Zero;
  270. actions = ;
  271. LastException = e;
  272. if (configuration.ThrowOnError)
  273. {
  274. throw;
  275. }
  276. return null;
  277. }
  278. }
  279.  
  280. public override void ResetItemTimeout(HttpContext context, string id)
  281. {
  282. try
  283. {
  284. if (LastException == null)
  285. {
  286. LogUtility.LogInfo("ResetItemTimeout => Session Id: {0}, Session provider object: {1}.", id, this.GetHashCode());
  287. GetAccessToStore(id);
  288. cache.UpdateExpiryTime((int)configuration.SessionTimeout.TotalSeconds);
  289. cache = null;
  290. }
  291. }
  292. catch (Exception e)
  293. {
  294. LogUtility.LogError("ResetItemTimeout => {0}", e.ToString());
  295. LastException = e;
  296. if (configuration.ThrowOnError)
  297. {
  298. throw;
  299. }
  300. }
  301. }
  302.  
  303. public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item)
  304. {
  305. try
  306. {
  307. if (LastException == null && lockId != null)
  308. {
  309. LogUtility.LogInfo("RemoveItem => Session Id: {0}, Session provider object: {1}.", id, this.GetHashCode());
  310. GetAccessToStore(id);
  311. cache.TryRemoveAndReleaseLockIfLockIdMatch(lockId);
  312. }
  313. }
  314. catch (Exception e)
  315. {
  316. LogUtility.LogError("RemoveItem => {0}", e.ToString());
  317. LastException = e;
  318. if (configuration.ThrowOnError)
  319. {
  320. throw;
  321. }
  322. }
  323. }
  324.  
  325. public override void ReleaseItemExclusive(HttpContext context, string id, object lockId)
  326. {
  327. try
  328. {
  329. // This check is required for unit tests to work
  330. int sessionTimeoutInSeconds;
  331. if (context != null && context.Session != null)
  332. {
  333. sessionTimeoutInSeconds = context.Session.Timeout * FROM_MIN_TO_SEC;
  334. }
  335. else
  336. {
  337. sessionTimeoutInSeconds = (int)configuration.SessionTimeout.TotalSeconds;
  338. }
  339.  
  340. if (LastException == null && lockId != null)
  341. {
  342. LogUtility.LogInfo("ReleaseItemExclusive => Session Id: {0}, Session provider object: {1} => For lockId: {2}.", id, this.GetHashCode(), lockId);
  343. GetAccessToStore(id);
  344. cache.TryReleaseLockIfLockIdMatch(lockId, sessionTimeoutInSeconds);
  345. // Either already released lock successfully inside above if block
  346. // Or we do not hold lock so we should not release it.
  347. sessionId = null;
  348. sessionLockId = null;
  349. }
  350. }
  351. catch (Exception e)
  352. {
  353. LogUtility.LogError("ReleaseItemExclusive => {0}", e.ToString());
  354. LastException = e;
  355. if (configuration.ThrowOnError)
  356. {
  357. throw;
  358. }
  359. }
  360. }
  361.  
  362. public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem)
  363. {
  364. try
  365. {
  366. if (LastException == null)
  367. {
  368. GetAccessToStore(id);
  369. // If it is new record
  370. if (newItem)
  371. {
  372. ISessionStateItemCollection sessionItems = null;
  373. if (item != null && item.Items != null)
  374. {
  375. sessionItems = item.Items;
  376. }
  377. else
  378. {
  379. sessionItems = new ChangeTrackingSessionStateItemCollection();
  380. }
  381.  
  382. if (sessionItems["SessionStateActions"] != null)
  383. {
  384. sessionItems.Remove("SessionStateActions");
  385. }
  386.  
  387. // Converting timout from min to sec
  388. cache.Set(sessionItems, (item.Timeout * FROM_MIN_TO_SEC));
  389. LogUtility.LogInfo("SetAndReleaseItemExclusive => Session Id: {0}, Session provider object: {1} => created new item in session.", id, this.GetHashCode());
  390. } // If update if lock matches
  391. else
  392. {
  393. if (item != null && item.Items != null)
  394. {
  395. if (item.Items["SessionStateActions"] != null)
  396. {
  397. item.Items.Remove("SessionStateActions");
  398. }
  399. // Converting timout from min to sec
  400. cache.TryUpdateAndReleaseLockIfLockIdMatch(lockId, item.Items, (item.Timeout * FROM_MIN_TO_SEC));
  401. LogUtility.LogInfo("SetAndReleaseItemExclusive => Session Id: {0}, Session provider object: {1} => updated item in session.", id, this.GetHashCode());
  402. }
  403. }
  404. }
  405. }
  406. catch (Exception e)
  407. {
  408. LogUtility.LogError("SetAndReleaseItemExclusive => {0}", e.ToString());
  409. LastException = e;
  410. if (configuration.ThrowOnError)
  411. {
  412. throw;
  413. }
  414. }
  415. }
  416. }
  417. }

其中GetItem和GetItemExclusive都是调用GetItemFromSessionStore方法,如果入口是GetItem调用TryCheckWriteLockAndGetData方法(不会发生锁),入口时GetItemExclusive调用TryTakeWriteLockAndGetData方法(会有锁),但是这2个方法都会修改Session的SessionTimeout值。

TryTakeWriteLockAndGetData方法的实现如下:

运行结果如下:

TryCheckWriteLockAndGetData的实现如下:

在GetItemFromSessionStore方法中如果获取ISessionStateItemCollection实例为null,我们调用 ReleaseItemExclusive(context, id, lockId)方法来释放锁(前提是前面调用TryTakeWriteLockAndGetData已经获取lockId),一般这个方法都不会执行的,现在我们知道默认情况下载装在session的时候就会锁,如果session实例为null我们会释放我们的锁。

那么这个锁又是是么时候释放的了?在 app.ReleaseRequestState += new EventHandler(this.OnReleaseState);会调用我们这里的SetAndReleaseItemExclusive方法,默认情况下它会释放我们的锁。

运行该方法结果如下:

其实现code如下:

RedisSessionStateProvider为了保证性能,在EndRequest里面还会尝试 释放锁。

到现在我们知道默认加载Session数据的时候会加锁,在ReleaseRequestState事件默认解锁。

asp.net mvc Session RedisSessionStateProvider锁的实现的更多相关文章

  1. asp.net mvc session锁问题

    一.会话状态Session Session用于服务器端状态管理,使用Session之后,每个客户端都可以将实际的数据保存在服务器上,对于每个客户端的数据,将会生成一个对应的唯一的key(保存在客户端) ...

  2. asp.net mvc session锁问题 (转载)

    一.会话状态Session Session用于服务器端状态管理,使用Session之后,每个客户端都可以将实际的数据保存在服务器上,对于每个客户端的数据,将会生成一个对应的唯一的key(保存在客户端) ...

  3. [转]菜鸟程序员之Asp.net MVC Session过期异常的处理

    本文转自:http://www.cnblogs.com/JustRun1983/p/3377652.html 小赵是刚毕业的计算机专业方面的大学生,4年的大学时间里面,他读过了很多编程方面的数据,也动 ...

  4. 菜鸟程序员之Asp.net MVC Session过期异常的处理

    小赵是刚毕业的计算机专业方面的大学生,4年的大学时间里面,他读过了很多编程方面的数据,也动手也了很多代码.现在毕业了,他如愿的加入了T公司,开始了自己的程序员生涯.他信心满满,相信自己4年的学习到的东 ...

  5. Ajax异步请求阻塞情况的解决办法(asp.net MVC Session锁的问题)

    讨论今天这个问题之前,我们先来看下浏览器公布的资源并发数限制个数,如下图 不难看出,目前主流浏览器支持都是最多6个并发 需要注意的是,浏览器的并发请求数目限制是针对同一域名的 意即,同一时间针对同一域 ...

  6. asp.net MVC Session锁的问题

    一直在用Session,对Session锁并没有太多的关注,可能是平时没有注意.前段时间突然发现,一个Ajax Get请求,直接访问地址,只需要几十ms,但是在页面中加载,却需要2s.最后定位到Ses ...

  7. Asp.Net MVC session跨域

    目的 在公司项目的某个特定场景中,需要在站点B的后端伪造请求,获取站点A的登录状态,抓取站点A的页面内容,因此要用实现session的跨域.以注册功能为例. 步骤 原理 简单地说,对于ASP.Net应 ...

  8. ASP.NET MVC Session 过期验证跳转至登入页面

    一.在要检查登入的控制器上继承 CheckLoginController 类 2. CheckLoginController 类的写法 using System; using System.Colle ...

  9. Asp.net MVC Session过期异常的处理

    一.使用MVC中的Filter来对Session进行验证 (1)方法1: public class MyAuthorizeAttribute : FilterAttribute, IAuthoriza ...

随机推荐

  1. java.util.Random 类

    //: object/ForEachFloat.java package object; import java.util.Random; public class ForEachFloat { pu ...

  2. php反射类的使用及Laravel对反射的使用介绍

    PHP的反射类与实例化对象作用相反,实例化是调用封装类中的方法.成员,而反射类则是拆封类中的所有方法.成员变量,并包括私有方法等.就如“解刨”一样,我们可以调用任何关键字修饰的方法.成员.当然在正常业 ...

  3. python 全栈开发,Day52(关于DOM操作的相关案例,JS中的面向对象,定时器,BOM,client、offset、scroll系列)

    昨日作业讲解: 京东购物车 京东购物车效果: 实现原理: 用2个盒子,就可以完整效果. 先让上面的小盒子向下移动1px,此时就出现了压盖效果.小盒子设置z-index压盖大盒子,将小盒子的下边框去掉, ...

  4. xxx is not in sudoers file 解决(转)

    解决方案:首需要切换到root身份$su -(注意有- ,这和su是不同的,在用命令"su"的时候只是切换到root,但没有把root的环境变量传过去,还是当前用户的环境变量,用& ...

  5. hdu 1588 求f(b) +f(k+b) +f(2k+b) +f((n-1)k +b) 之和 (矩阵快速幂)

    g(i)=k*i+b; 0<=i<nf(0)=0f(1)=1f(n)=f(n-1)+f(n-2) (n>=2)求f(b) +f(k+b) +f(2*k+b) +f((n-1)*k + ...

  6. java快速排序引起的StackOverflowError异常

    写在前面:这篇随笔主要记录一下递归调用引起的虚拟机栈溢出的情况以及通过参数配置了虚拟机栈大小来使递归调用可以顺利执行.并没有对涉及到的一些概念进行详细的解释(因为我自己目前对这些概念并不是特别清楚), ...

  7. FFT 【JSOI2012】bzoj4332 分零食 (未解决)

    很不错的一道倍增优化dp?? 第一次做这类题挺难想的 题目大意: 有n个小朋友,m块糖. 给小朋友分糖,如果一个小朋友分不到糖,那他后面的小朋友也分不到糖. 每个小朋友有一个喜悦值,有三个参数,O,S ...

  8. Docker+Jenkins持续集成环境(1)使用Docker搭建Jenkins+Docker持续集成环境

    本文介绍如何通过Jenkins的docker镜像从零开始构建一个基于docker镜像的持续集成环境,包含自动化构建.发布到仓库\并部署上线. 0. 前置条件 服务器安装docker,并启动docker ...

  9. C# 收发和处理自定义的WINDOWS消息

    C# 发送.接收和处理自定义的WINDOWS消息 转载地址:http://blog.chinaunix.net/uid-24427209-id-2608350.html 为了程序启动后自动执行主函数, ...

  10. asp.net core服务的生命周期

    Transient:每一次GetService都会创建一个新的实例 Scoped:在同一个Scope内只初始化一个实例 ,可以理解为( 每一个request级别只创建一个实例,同一个http requ ...