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

  • 相关阅读:
    解决云服务器ECS,windows server 2012不能安装SQL Server 2012,不能安装.NET Fromework 3.5
    html5中checkbox的选中状态的设置与获取
    sql server 韩文查询匹配失败
    管理nuget程序包中搜索不到任何程序包
    ftp下出现“当前的安全设置不允许从该位置下载文件”提示
    windows server 2012 下IIS8.5关于“ 配置错误 不能在此路径中使用此配置节”的解决办法
    服务器升级后访问网站资源返回404
    centos7yum的更新与优化
    linux(centos7)命令提示符优化
    检查vmware虚拟软件服务是否开启?
  • 原文地址:https://www.cnblogs.com/majiang/p/6526822.html
Copyright © 2020-2023  润新知