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
    }
}

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

MvcRouteHandler的code:

  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

  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

  View Code

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

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

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值