zoukankan      html  css  js  c++  java
  • asp.net mvc Session RedisSessionStateProvider锁的实现

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

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

    namespace System.Web.Routing {
        using System.Diagnostics;
        using System.Globalization;
        using System.Runtime.CompilerServices;
        using System.Web.Security;
    
        [TypeForwardedFrom("System.Web.Routing, Version=3.5.0.0, Culture=Neutral, PublicKeyToken=31bf3856ad364e35")]
        public class UrlRoutingModule : IHttpModule {
            private static readonly object _contextKey = new Object();
            private static readonly object _requestDataKey = new Object();
            private RouteCollection _routeCollection;
    
            [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly",
                Justification = "This needs to be settable for unit tests.")]
            public RouteCollection RouteCollection {
                get {
                    if (_routeCollection == null) {
                        _routeCollection = RouteTable.Routes;
                    }
                    return _routeCollection;
                }
                set {
                    _routeCollection = value;
                }
            }
    
            protected virtual void Dispose() {
            }
    
            protected virtual void Init(HttpApplication application) {
    
                //////////////////////////////////////////////////////////////////
                // Check if this module has been already addded
                if (application.Context.Items[_contextKey] != null) {
                    return; // already added to the pipeline
                }
                application.Context.Items[_contextKey] = _contextKey;
    
                // Ideally we would use the MapRequestHandler event.  However, MapRequestHandler is not available
                // in II6 or IIS7 ISAPI Mode.  Instead, we use PostResolveRequestCache, which is the event immediately
                // before MapRequestHandler.  This allows use to use one common codepath for all versions of IIS.
                application.PostResolveRequestCache += OnApplicationPostResolveRequestCache;
            }
    
            private void OnApplicationPostResolveRequestCache(object sender, EventArgs e) {
                HttpApplication app = (HttpApplication)sender;
                HttpContextBase context = new HttpContextWrapper(app.Context);
                PostResolveRequestCache(context);
            }
    
            [Obsolete("This method is obsolete. Override the Init method to use the PostMapRequestHandler event.")]
            public virtual void PostMapRequestHandler(HttpContextBase context) {
                // Backwards compat with 3.5 which used to have code here to Rewrite the URL
            }
    
            public virtual void PostResolveRequestCache(HttpContextBase context) {
                // Match the incoming URL against the route table
                RouteData routeData = RouteCollection.GetRouteData(context);
    
                // Do nothing if no route found
                if (routeData == null) {
                    return;
                }
    
                // If a route was found, get an IHttpHandler from the route's RouteHandler
                IRouteHandler routeHandler = routeData.RouteHandler;
                if (routeHandler == null) {
                    throw new InvalidOperationException(
                        String.Format(
                            CultureInfo.CurrentCulture,
                            SR.GetString(SR.UrlRoutingModule_NoRouteHandler)));
                }
    
                // This is a special IRouteHandler that tells the routing module to stop processing
                // routes and to let the fallback handler handle the request.
                if (routeHandler is StopRoutingHandler) {
                    return;
                }
    
                RequestContext requestContext = new RequestContext(context, routeData);
    
                // Dev10 766875    Adding RouteData to HttpContext
                context.Request.RequestContext = requestContext;
    
                IHttpHandler httpHandler = routeHandler.GetHttpHandler(requestContext);
                if (httpHandler == null) {
                    throw new InvalidOperationException(
                        String.Format(
                            CultureInfo.CurrentUICulture,
                            SR.GetString(SR.UrlRoutingModule_NoHttpHandler),
                            routeHandler.GetType()));
                }
    
                if (httpHandler is UrlAuthFailureHandler) {
                    if (FormsAuthenticationModule.FormsAuthRequired) {
                        UrlAuthorizationModule.ReportUrlAuthorizationFailure(HttpContext.Current, this);
                        return;
                    }
                    else {
                        throw new HttpException(401, SR.GetString(SR.Assess_Denied_Description3));
                    }
                }
    
                // Remap IIS7 to our handler
                context.RemapHandler(httpHandler);
            }
    
            #region IHttpModule Members
            void IHttpModule.Dispose() {
                Dispose();
            }
    
            void IHttpModule.Init(HttpApplication application) {
                Init(application);
            }
            #endregion
        }
    }
    View Code

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

    MvcRouteHandler的code:

    // Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
    
    using System.Web.Mvc.Properties;
    using System.Web.Routing;
    using System.Web.SessionState;
    
    namespace System.Web.Mvc
    {
        public class MvcRouteHandler : IRouteHandler
        {
            private IControllerFactory _controllerFactory;
    
            public MvcRouteHandler()
            {
            }
    
            public MvcRouteHandler(IControllerFactory controllerFactory)
            {
                _controllerFactory = controllerFactory;
            }
    
            protected virtual IHttpHandler GetHttpHandler(RequestContext requestContext)
            {
                requestContext.HttpContext.SetSessionStateBehavior(GetSessionStateBehavior(requestContext));
                return new MvcHandler(requestContext);
            }
    
            protected virtual SessionStateBehavior GetSessionStateBehavior(RequestContext requestContext)
            {
                string controllerName = (string)requestContext.RouteData.Values["controller"];
                if (String.IsNullOrWhiteSpace(controllerName))
                {
                    throw new InvalidOperationException(MvcResources.MvcRouteHandler_RouteValuesHasNoController);
                }
    
                IControllerFactory controllerFactory = _controllerFactory ?? ControllerBuilder.Current.GetControllerFactory();
                return controllerFactory.GetControllerSessionBehavior(requestContext, controllerName);
            }
    
            #region IRouteHandler Members
    
            IHttpHandler IRouteHandler.GetHttpHandler(RequestContext requestContext)
            {
                return GetHttpHandler(requestContext);
            }
    
            #endregion
        }
    }
    View Code

    在MvcRouteHandler中GetHttpHandler设置SessionStateBehavior:

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

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

     SessionStateBehavior IControllerFactory.GetControllerSessionBehavior(RequestContext requestContext, string controllerName)
            {
                if (requestContext == null)
                {
                    throw new ArgumentNullException("requestContext");
                }
                if (String.IsNullOrEmpty(controllerName))
                {
                    throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
                }
    
                Type controllerType = GetControllerType(requestContext, controllerName);
                return GetControllerSessionBehavior(requestContext, controllerType);
            }
            
            protected internal virtual SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
            {
                if (controllerType == null)
                {
                    return SessionStateBehavior.Default;
                }
    
                return _sessionStateCache.GetOrAdd(
                    controllerType,
                    type =>
                    {
                        var attr = type.GetCustomAttributes(typeof(SessionStateAttribute), inherit: true)
                            .OfType<SessionStateAttribute>()
                            .FirstOrDefault();
    
                        return (attr != null) ? attr.Behavior : SessionStateBehavior.Default;
                    });
            }
    View Code

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

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

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

    所以默认RequiresSessionState为true,ReadOnlySessionState为false

    public IHttpHandler Handler {
                get { return _handler;}
                set {
                    _handler = value;
                    _requiresSessionStateFromHandler = false;
                    _readOnlySessionStateFromHandler = false;
                    InAspCompatMode = false;
                    if (_handler != null) {
                        if (_handler is IRequiresSessionState) {
                            _requiresSessionStateFromHandler = true;
                        }
                        if (_handler is IReadOnlySessionState) {
                            _readOnlySessionStateFromHandler = true;
                        }
                        Page page = _handler as Page;
                        if (page != null && page.IsInAspCompatMode) {
                            InAspCompatMode = true;
                        }
                    }
                }
            }
            
    // session state support
            private bool _requiresSessionStateFromHandler;
            internal bool RequiresSessionState {
                get {
                    switch (SessionStateBehavior) {
                        case SessionStateBehavior.Required:
                        case SessionStateBehavior.ReadOnly:
                            return true;
                        case SessionStateBehavior.Disabled:
                            return false;
                        case SessionStateBehavior.Default:
                        default:
                            return _requiresSessionStateFromHandler;
                    }
                }
            }
    
            private bool _readOnlySessionStateFromHandler;
            internal bool ReadOnlySessionState {
                get {
                    switch (SessionStateBehavior) {
                        case SessionStateBehavior.ReadOnly:
                            return true;
                        case SessionStateBehavior.Required:
                        case SessionStateBehavior.Disabled:
                            return false;
                        case SessionStateBehavior.Default:
                        default:
                            return _readOnlySessionStateFromHandler;
                    }
                }
            }        
    View Code

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

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

    //
    // Copyright (c) Microsoft Corporation.  All rights reserved.
    // Licensed under the MIT License. See License.txt in the project root for license information.
    //
    
    using System;
    using System.Web;
    using System.Web.SessionState;
    
    namespace Microsoft.Web.Redis
    {
        public class RedisSessionStateProvider : SessionStateStoreProviderBase
        {
            // 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. 
            // So we are going to store 'sessionId' and 'lockId' when we acquire lock. so that EndRequest can release lock at the end. 
            // 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).
            internal string sessionId;
            internal object sessionLockId;
            private const int FROM_MIN_TO_SEC = 60;
            
            internal static ProviderConfiguration configuration;
            internal static object configurationCreationLock = new object();
            internal ICacheConnection cache;
    
            private static object _lastException = new object();
    
            /// <summary>
            /// 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.
            /// 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
            /// 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.
            /// </summary>
            public static Exception LastException
            {
                get 
                {
                    if (HttpContext.Current != null)
                    {
                        return (Exception) HttpContext.Current.Items[_lastException];
                    }
                    return null;
                }
    
                set
                {
                    if (HttpContext.Current != null)
                    {
                        HttpContext.Current.Items[_lastException] = value;
                    }             
                }
            }
    
            private void GetAccessToStore(string id) 
            {
                if (cache == null)
                {
                    cache = new RedisConnectionWrapper(configuration, id);
                }
                else
                {
                    cache.Keys.RegenerateKeyStringIfIdModified(id, configuration.ApplicationName);
                }
            }
    
            public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
            {
                if (config == null)
                { 
                    throw new ArgumentNullException("config");
                }
                
                if (name == null || name.Length == 0)
                {
                    name = "MyCacheStore";
                }
                
                if (String.IsNullOrEmpty(config["description"]))
                {
                    config.Remove("description");
                    config.Add("description", "Redis as a session data store");
                }
    
                base.Initialize(name, config);
    
                // If configuration exists then use it otherwise read from config file and create one
                if (configuration == null)
                {
                    lock (configurationCreationLock) 
                    {
                        if (configuration == null)
                        {
                            configuration = ProviderConfiguration.ProviderConfigurationForSessionState(config);
                        }
                    }
                }
            }
    
            public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback)
            {
                //We don't receive notifications when cache items expire, so we can't support Session_OnEnd.
                return false;
            }
    
            public override void InitializeRequest(HttpContext context)
            {
                //Not need. Initializing in 'Initialize method'.
            }
    
            public override void Dispose()
            {
                //Not needed. Cleanup is done in 'EndRequest'.
            }
    
            public override void EndRequest(HttpContext context)
            {
                try
                {
                    // This check is required for unit tests to work
                    int sessionTimeoutInSeconds;
                    if (context != null && context.Session != null)
                    {
                        sessionTimeoutInSeconds = context.Session.Timeout * FROM_MIN_TO_SEC;
                    }
                    else
                    {
                        sessionTimeoutInSeconds = (int)configuration.SessionTimeout.TotalSeconds;
                    }
    
                    if (sessionId != null && sessionLockId != null)
                    {
                        GetAccessToStore(sessionId);
                        cache.TryReleaseLockIfLockIdMatch(sessionLockId, sessionTimeoutInSeconds);
                        LogUtility.LogInfo("EndRequest => Session Id: {0}, Session provider object: {1} => Lock Released with lockId {2}.", sessionId, this.GetHashCode(), sessionLockId);
                        sessionId = null;
                        sessionLockId = null;
                    }
                    cache = null;
                }
                catch (Exception e)
                {
                    LogUtility.LogError("EndRequest => {0}", e.ToString());
                    LastException = e;
                    if (configuration.ThrowOnError)
                    {
                        throw;
                    }
                }
            }
    
            public override SessionStateStoreData CreateNewStoreData(HttpContext context, int timeout)
            {
                //Creating empty session store data and return it. 
                LogUtility.LogInfo("CreateNewStoreData => Session provider object: {0}.", this.GetHashCode());
                return new SessionStateStoreData(new ChangeTrackingSessionStateItemCollection(), new HttpStaticObjectsCollection(), timeout);
            }
            
            public override void CreateUninitializedItem(HttpContext context, string id, int timeout)
            {
                try
                {
                    if (LastException == null)
                    {
                        LogUtility.LogInfo("CreateUninitializedItem => Session Id: {0}, Session provider object: {1}.", id, this.GetHashCode());
                        ISessionStateItemCollection sessionData = new ChangeTrackingSessionStateItemCollection();
                        sessionData["SessionStateActions"] = SessionStateActions.InitializeItem;
                        GetAccessToStore(id);
                        // Converting timout from min to sec
                        cache.Set(sessionData, (timeout * FROM_MIN_TO_SEC));
                    }
                }
                catch (Exception e)
                {
                    LogUtility.LogError("CreateUninitializedItem => {0}", e.ToString());
                    LastException = e;
                    if (configuration.ThrowOnError)
                    {
                        throw;
                    }
                }
            }
            
            public override SessionStateStoreData GetItem(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
            {
                LogUtility.LogInfo("GetItem => Session Id: {0}, Session provider object: {1}.", id, this.GetHashCode());
                return GetItemFromSessionStore(false, context, id, out locked, out lockAge, out lockId, out actions);
            }
    
            public override SessionStateStoreData GetItemExclusive(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
            {
                LogUtility.LogInfo("GetItemExclusive => Session Id: {0}, Session provider object: {1}.", id, this.GetHashCode());
                return GetItemFromSessionStore(true, context, id, out locked, out lockAge, out lockId, out actions);
            }
    
            private SessionStateStoreData GetItemFromSessionStore(bool isWriteLockRequired, HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions)
            {
                try
                {
                    SessionStateStoreData sessionStateStoreData = null;
                    locked = false;
                    lockAge = TimeSpan.Zero;
                    lockId = 0;
                    actions = SessionStateActions.None;
                    if (id == null)
                    {
                        return null;
                    }
                    GetAccessToStore(id);
                    ISessionStateItemCollection sessionData = null;
                
                    int sessionTimeout;
                    bool isLockTaken = false;
                    //Take read or write lock and if locking successful than get data in sessionData and also update session timeout
                    if (isWriteLockRequired)
                    {
                        isLockTaken = cache.TryTakeWriteLockAndGetData(DateTime.Now, (int)configuration.RequestTimeout.TotalSeconds, out lockId, out sessionData, out sessionTimeout);
                        sessionId = id; // signal that we have to remove lock in EndRequest
                        sessionLockId = lockId; // save lockId for EndRequest
                    }
                    else
                    {
                        isLockTaken = cache.TryCheckWriteLockAndGetData(out lockId, out sessionData, out sessionTimeout);
                    }
    
                    if (isLockTaken)
                    {
                        locked = false;
                        LogUtility.LogInfo("GetItemFromSessionStore => Session Id: {0}, Session provider object: {1} => Lock taken with lockId: {2}", id, this.GetHashCode(), lockId);
                    }
                    else
                    {
                        sessionId = null;
                        sessionLockId = null;
                        locked = true;
                        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);
                    }
    
                    // If locking is not successful then do not return any result just return lockAge, locked=true and lockId.
                    // ASP.NET tries to acquire lock again in 0.5 sec by calling this method again. Using lockAge it finds if 
                    // lock has been taken more than http request timeout than ASP.NET calls ReleaseItemExclusive and calls this method again to get lock.
                    if (locked) 
                    {
                        lockAge = cache.GetLockAge(lockId);
                        return null;
                    }
    
                    if (sessionData == null)
                    {
                        // 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.
                        // But we just locked the record so first release it
                        ReleaseItemExclusive(context, id, lockId);
                        return null;
                    }
                
                    // Restore action flag from session data
                    if (sessionData["SessionStateActions"] != null) 
                    {
                        actions = (SessionStateActions)Enum.Parse(typeof(SessionStateActions), sessionData["SessionStateActions"].ToString());
                    }
    
                    //Get data related to this session from sessionDataDictionary and populate session items
                    sessionData.Dirty = false;
                    sessionStateStoreData = new SessionStateStoreData(sessionData, new HttpStaticObjectsCollection(), sessionTimeout);
                    return sessionStateStoreData;
                }
                catch (Exception e)
                {
                    LogUtility.LogError("GetItemFromSessionStore => {0}", e.ToString());
                    locked = false;
                    lockId = null;
                    lockAge = TimeSpan.Zero;
                    actions = 0;
                    LastException = e;
                    if (configuration.ThrowOnError)
                    {
                        throw;
                    }
                    return null;
                }
            }
           
            public override void ResetItemTimeout(HttpContext context, string id) 
            {
                try
                {
                    if (LastException == null)
                    {
                        LogUtility.LogInfo("ResetItemTimeout => Session Id: {0}, Session provider object: {1}.", id, this.GetHashCode());
                        GetAccessToStore(id);
                        cache.UpdateExpiryTime((int)configuration.SessionTimeout.TotalSeconds);
                        cache = null;
                    }
                }
                catch (Exception e)
                {
                    LogUtility.LogError("ResetItemTimeout => {0}", e.ToString());
                    LastException = e;
                    if (configuration.ThrowOnError)
                    {
                        throw;
                    }
                }
            }
    
            public override void RemoveItem(HttpContext context, string id, object lockId, SessionStateStoreData item)
            {
                try
                {
                    if (LastException == null && lockId != null)
                    {
                        LogUtility.LogInfo("RemoveItem => Session Id: {0}, Session provider object: {1}.", id, this.GetHashCode());
                        GetAccessToStore(id);
                        cache.TryRemoveAndReleaseLockIfLockIdMatch(lockId);
                    }
                }
                catch (Exception e)
                {
                    LogUtility.LogError("RemoveItem => {0}", e.ToString());
                    LastException = e;
                    if (configuration.ThrowOnError)
                    {
                        throw;
                    }
                }
            }
    
            public override void ReleaseItemExclusive(HttpContext context, string id, object lockId)
            {
                try
                {
                    // This check is required for unit tests to work
                    int sessionTimeoutInSeconds;
                    if (context != null && context.Session != null)
                    {
                        sessionTimeoutInSeconds = context.Session.Timeout * FROM_MIN_TO_SEC;
                    }
                    else
                    {
                        sessionTimeoutInSeconds = (int)configuration.SessionTimeout.TotalSeconds;
                    }
    
                    if (LastException == null && lockId != null)
                    {
                        LogUtility.LogInfo("ReleaseItemExclusive => Session Id: {0}, Session provider object: {1} => For lockId: {2}.", id, this.GetHashCode(), lockId);
                        GetAccessToStore(id);
                        cache.TryReleaseLockIfLockIdMatch(lockId, sessionTimeoutInSeconds);
                        // Either already released lock successfully inside above if block
                        // Or we do not hold lock so we should not release it.
                        sessionId = null;
                        sessionLockId = null;
                    }
                }
                catch (Exception e)
                {
                    LogUtility.LogError("ReleaseItemExclusive => {0}", e.ToString());
                    LastException = e;
                    if (configuration.ThrowOnError)
                    {
                        throw;
                    }
                }
            }
            
            public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem)
            {
                try
                {
                    if (LastException == null)
                    {
                        GetAccessToStore(id);
                        // If it is new record
                        if (newItem)
                        {
                            ISessionStateItemCollection sessionItems = null;
                            if (item != null && item.Items != null)
                            {
                                sessionItems = item.Items;
                            }
                            else
                            {
                                sessionItems = new ChangeTrackingSessionStateItemCollection();
                            }
    
                            if (sessionItems["SessionStateActions"] != null)
                            {
                                sessionItems.Remove("SessionStateActions");
                            }
    
                            // Converting timout from min to sec
                            cache.Set(sessionItems, (item.Timeout * FROM_MIN_TO_SEC));
                            LogUtility.LogInfo("SetAndReleaseItemExclusive => Session Id: {0}, Session provider object: {1} => created new item in session.", id, this.GetHashCode());
                        } // If update if lock matches
                        else
                        {
                            if (item != null && item.Items != null)
                            {
                                if (item.Items["SessionStateActions"] != null)
                                {
                                    item.Items.Remove("SessionStateActions");
                                }
                                // Converting timout from min to sec
                                cache.TryUpdateAndReleaseLockIfLockIdMatch(lockId, item.Items, (item.Timeout * FROM_MIN_TO_SEC));
                                LogUtility.LogInfo("SetAndReleaseItemExclusive => Session Id: {0}, Session provider object: {1} => updated item in session.", id, this.GetHashCode());
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    LogUtility.LogError("SetAndReleaseItemExclusive => {0}", e.ToString());
                    LastException = e;
                    if (configuration.ThrowOnError)
                    {
                        throw;
                    }
                }
            }
        }
    }
    View Code

    其中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事件默认解锁。   

  • 相关阅读:
    Dynamics AX 2012 R2 配置E-Mail模板
    Dynamics AX 2012 R2 设置E-Mail
    Dynamics AX 2012 R2 为运行失败的批处理任务设置预警
    Dynamics AX 2012 R2 耗尽用户
    Dynamics AX 2012 R2 创建一个专用的批处理服务器
    Dynamics AX 2012 R2 创建一个带有负载均衡的服务器集群
    Dynamics AX 2012 R2 安装额外的AOS
    Dynamics AX 2012 R2 将系统用户账号连接到工作人员记录
    Dynamics AX 2012 R2 从代码中调用SSRS Report
    Dynamics AX 2012 R2 IIS WebSite Unauthorized 401
  • 原文地址:https://www.cnblogs.com/majiang/p/6526822.html
Copyright © 2011-2022 走看看