• Asp.net Session认识加强Session究竟是如何存储你知道吗?


    我们还是简单的来复习一下Session吧:Session的数据时保存在服务器端,并且每个客户端对应不同Session。那么Session究竟是如何保存,如何区分客服端的了?我们还是沿用以前的方法来讲吧,以一个demo开始:

        protected void Page_Load(object sender, EventArgs e)  
               {  
                   string name = this.Request["Name"];  
                   object sessionName = Session["Name"];  
                   if (string.IsNullOrEmpty(name) && sessionName==null)  
                   {  
                       Response.Write("Please Enter your name!");  
                   }  
                   else   
                   {  
                       if (sessionName == null)  
                       {  
                           Session.Add("Name", name);  
                           Response.Write("Set Session name and Session ID:"+Session.SessionID);  
                       }  
                       else  
                       {  
                           Response.Write("Get Session Name and Session ID:"+ Session.SessionID);  
          
                       }  
                       Response.Write(" Name:" + name);  
                   }  
               }  
    

     假设我们的请求路径为http://localhost:18385/WebForm1.aspx?name=majiang

    第一次请求数据如下:

    第二次请求数据了:

    这里我们看见在第一次请求的返回头里面有一个ASP.NET_SessionId,在第二次请求过程中这个请求头里面也含有ASP.NET_SessionId,并且它的值刚好是Session.SessionID(我这里用的是asp.net4.5),我们可以猜测这个ASP.NET_SessionId就是用来区分不同的客户端请求。那么这个值是什么时候生成的又是什么时候输出的了?

    首先我们需要知道我们用到的那个Session具体在什么地方定义的,其实它是定义于HttpContext的Session属性中,我们一般的page也只是调用这个属性而已。

    public HttpSessionState Session
    {
        get
        {
            if (this.HasWebSocketRequestTransitionCompleted)
            {
                return null;
            }
            if (this._sessionStateModule != null)
            {
                lock (this)
                {
                    if (this._sessionStateModule != null)
                    {
                        this._sessionStateModule.InitStateStoreItem(true);
                        this._sessionStateModule = null;
                    }
                }
            }
            return (HttpSessionState) this.Items["AspSession"];
        }
    }
     
    这里用到一个_sessionStateModule的变量,那么究竟在什么地方操作它们的了?在HttpContext中有两个操作sessionStateModule方法如下:
      internal void AddDelayedHttpSessionState(SessionStateModule module)
        {
            if (this._sessionStateModule != null)
            {
                throw new HttpException(SR.GetString("Cant_have_multiple_session_module"));
            }
            this._sessionStateModule = module;
        }

        internal void RemoveDelayedHttpSessionState()
        {
            this._sessionStateModule = null;
        }

    这两个方法干什么的我就不说了,它们是在什么地方调用的了。如果你开发过asp.net,那么你应该知道在SessionStateModule 类,它是一个IHttpModule的实现者专门用来管理Session的,在这个类中有一个InitModuleFromConfig方法,该方法主要 是在该类的Init中调用,如丧我们来看看它的具体实现吧:

        private void InitModuleFromConfig(HttpApplication app, SessionStateSection config)  
           {  
               if (config.Mode != SessionStateMode.Off)  
               {  
                   app.AddOnAcquireRequestStateAsync(new BeginEventHandler(this.BeginAcquireState), new EndEventHandler(this.EndAcquireState));  
                   app.ReleaseRequestState += new EventHandler(this.OnReleaseState);  
                   app.EndRequest += new EventHandler(this.OnEndRequest);  
                   this._partitionResolver = this.InitPartitionResolver(config);  
                   switch (config.Mode)  
                   {  
                       case SessionStateMode.InProc:  
                           if (HttpRuntime.UseIntegratedPipeline)  
                           {  
                               s_canSkipEndRequestCall = true;  
                           }  
                           this._store = new InProcSessionStateStore();  
                           this._store.Initialize(null, null);  
                           break;  
          
                       case SessionStateMode.StateServer:  
                           if (HttpRuntime.UseIntegratedPipeline)  
                           {  
                               s_canSkipEndRequestCall = true;  
                           }  
                           this._store = new OutOfProcSessionStateStore();  
                           ((OutOfProcSessionStateStore) this._store).Initialize(null, null, this._partitionResolver);  
                           break;  
          
                       case SessionStateMode.SQLServer:  
                           this._store = new SqlSessionStateStore();  
                           ((SqlSessionStateStore) this._store).Initialize(null, null, this._partitionResolver);  
                           break;  
          
                       case SessionStateMode.Custom:  
                           this._store = this.InitCustomStore(config);  
                           break;  
                   }  
                   this._idManager = this.InitSessionIDManager(config);  
                   if (((config.Mode == SessionStateMode.InProc) || (config.Mode == SessionStateMode.StateServer)) && this._usingAspnetSessionIdManager)  
                   {  
                       this._ignoreImpersonation = true;  
                   }  
               }  
           }  

    这里主要是设置 this._store和  this._idManager 它们两个变量,其中 this._store的设置根据Session的存储类型不同设置为不同的实例,这里的存储方式有以下四种

    public enum SessionStateMode
    {
        Off,
        InProc,
        StateServer,
        SQLServer,
        Custom
    }
    默认的是SessionStateMode.InProc,所以默认的this._store是一个InProcSessionStateStore实 例,而this._idManager默认是一个SessionIDManager实例。这个方法结束后我们的 this._store和  this._idManager这两个变量就已经有值了。在SessionStateModule类中还有一个很重要的方法 BeginAcquireState:

        private IAsyncResult BeginAcquireState(object source, EventArgs e, AsyncCallback cb, object extraData)  
           {  
               IAsyncResult result;  
               bool sessionStateItem = true;  
               bool flag3 = false;  
               this._acquireCalled = true;  
               this._releaseCalled = false;  
               this.ResetPerRequestFields();  
               this._rqContext = ((HttpApplication) source).Context;  
               this._rqAr = new HttpAsyncResult(cb, extraData);  
               this.ChangeImpersonation(this._rqContext, false);  
               try  
               {  
                   if (EtwTrace.IsTraceEnabled(4, 8))  
                   {  
                       EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_BEGIN, this._rqContext.WorkerRequest);  
                   }  
                   this._store.InitializeRequest(this._rqContext);  
                   bool requiresSessionState = this._rqContext.RequiresSessionState;  
                   if (this._idManager.InitializeRequest(this._rqContext, false, out this._rqSupportSessionIdReissue))  
                   {  
                       this._rqAr.Complete(true, null, null);  
                       if (EtwTrace.IsTraceEnabled(4, 8))  
                       {  
                           EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, this._rqContext.WorkerRequest);  
                       }  
                       return this._rqAr;  
                   }  
                   if ((s_allowInProcOptimization && !s_sessionEverSet) && (!requiresSessionState || !((SessionIDManager) this._idManager).UseCookieless(this._rqContext)))  
                   {  
                       flag3 = true;  
                   }  
                   else  
                   {  
                       this._rqId = this._idManager.GetSessionID(this._rqContext);  
                   }  
                   if (!requiresSessionState)  
                   {  
                       if (this._rqId != null)  
                       {  
                           this._store.ResetItemTimeout(this._rqContext, this._rqId);  
                       }  
                       this._rqAr.Complete(true, null, null);  
                       if (EtwTrace.IsTraceEnabled(4, 8))  
                       {  
                           EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, this._rqContext.WorkerRequest);  
                       }  
                       return this._rqAr;  
                   }  
                   this._rqExecutionTimeout = this._rqContext.Timeout;  
                   if (this._rqExecutionTimeout == DEFAULT_DBG_EXECUTION_TIMEOUT)  
                   {  
                       this._rqExecutionTimeout = s_configExecutionTimeout;  
                   }  
                   this._rqReadonly = this._rqContext.ReadOnlySessionState;  
                   if (this._rqId != null)  
                   {  
                       sessionStateItem = this.GetSessionStateItem();  
                   }  
                   else if (!flag3)  
                   {  
                       bool flag4 = this.CreateSessionId();  
                       this._rqIdNew = true;  
                       if (flag4)  
                       {  
                           if (s_configRegenerateExpiredSessionId)  
                           {  
                               this.CreateUninitializedSessionState();  
                           }  
                           this._rqAr.Complete(true, null, null);  
                           if (EtwTrace.IsTraceEnabled(4, 8))  
                           {  
                               EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, this._rqContext.WorkerRequest);  
                           }  
                           return this._rqAr;  
                       }  
                   }  
                   if (sessionStateItem)  
                   {  
                       this.CompleteAcquireState();  
                       this._rqAr.Complete(true, null, null);  
                   }  
                   result = this._rqAr;  
               }  
               finally  
               {  
                   this.RestoreImpersonation();  
               }  
               return result;  
           }  

    在这个方法中有以下3句比较重要

        this._rqId = this._idManager.GetSessionID(this._rqContext);
       sessionStateItem = this.GetSessionStateItem();

        this.CompleteAcquireState();

    第一句获取SessionID,第二句货物SessionStateItem,第三句主要是调用一个CompleteAcquireState方法,而这个方法里面有一句  SessionStateUtility.AddDelayedHttpSessionStateToContext(this._rqContext, this);或则this.InitStateStoreItem(true); 这个方法主要对应一句

     SessionStateUtility.AddHttpSessionStateToContext(this._rqContext, this._rqSessionState);,在这个类中还有一个方法OnReleaseState里面有这么一句

     SessionStateUtility.RemoveHttpSessionStateFromContext(this._rqContext, delayed);

    我们首先来可看看SessionStateUtility的AddHttpSessionStateToContext、RemoveHttpSessionStateFromContext方法的实现吧。

    internal static void AddDelayedHttpSessionStateToContext(HttpContext context, SessionStateModule module)
    {
        context.AddDelayedHttpSessionState(module);
    }
    internal void AddDelayedHttpSessionState(SessionStateModule module)
    {
        if (this._sessionStateModule != null)
        {
            throw new HttpException(SR.GetString("Cant_have_multiple_session_module"));
        }
        this._sessionStateModule = module;
    }
    
    public static void AddHttpSessionStateToContext(HttpContext context, IHttpSessionState container)
        {
            HttpSessionState state = new HttpSessionState(container);
            try
            {
                context.Items.Add("AspSession", state);
            }
            catch (ArgumentException)
            {
                throw new HttpException(SR.GetString("Cant_have_multiple_session_module"));
            }
        }
    
      internal static void RemoveHttpSessionStateFromContext(HttpContext context, bool delayed)
        {
            if (delayed)
            {
                context.RemoveDelayedHttpSessionState();
            }
            else
            {
                context.Items.Remove("AspSession");
            }
        }

    其中HttpContext的RemoveDelayedHttpSessionState就一句    this._sessionStateModule = null;我想对于SessionStateUtility里面的这几个方法我就不多说吧,很简单。

    我们还是回头看看前面那2句吧,

    public string GetSessionID(HttpContext context)
    {
        string id = null;
        this.CheckInitializeRequestCalled(context);
        if (this.UseCookieless(context))
        {
            return (string) context.Items["AspCookielessSession"];
        }
        HttpCookie cookie = context.Request.Cookies[Config.CookieName];
        if ((cookie != null) && (cookie.Value != null))
        {
            id = this.Decode(cookie.Value);
            if ((id != null) && !this.ValidateInternal(id, false))
            {
                id = null;
            }
        }
        return id;
    }

    默认情况下我们的cookie是可用的,这里的Config.CookieName实际上就是SessionStateSection的CookieName属性

    服务器保存的id就是cookie value过后的Decode,其实现code 如下:

     public virtual String Decode(String id) {
                // Need to do UrlDecode if the session id could be custom created.
                if (_isInherited) {
                    Debug.Trace("SessionIDManager", "Decode is doing UrlDecode ");
                    return HttpUtility.UrlDecode(id);
                }
                else {
                    Debug.Trace("SessionIDManager", "Decode is doing nothing");
                    return id.ToLower(CultureInfo.InvariantCulture);
                }
            }

      其中  _isInherited = !(this.GetType() == typeof(SessionIDManager));的取值。SessionIDManager的实例代码如下

      ISessionIDManager InitSessionIDManager(SessionStateSection config) {
                string  sessionIDManagerType = config.SessionIDManagerType;
                ISessionIDManager  iManager;
    
                if (String.IsNullOrEmpty(sessionIDManagerType)) {
                    iManager = new SessionIDManager();
                    _usingAspnetSessionIdManager = true;
                }
                else {
                    Type    managerType;
    
                    managerType = ConfigUtil.GetType(sessionIDManagerType, "sessionIDManagerType", config);
                    ConfigUtil.CheckAssignableType(typeof(ISessionIDManager), managerType, config, "sessionIDManagerType");
    
                    iManager = (ISessionIDManager)HttpRuntime.CreatePublicInstance(managerType);
                }
    
                iManager.Initialize();
    
                return iManager;
            }
    [ConfigurationProperty("cookieName", DefaultValue="ASP.NET_SessionId")]
    public string CookieName
    {
        get
        {
            return (string) base[_propCookieName];
        }
        set
        {
            base[_propCookieName] = value;
        }
    }

    到这里大家应该知道为什么Http请求和返回关于Session对应Cookie的id是ASP.NET_SessionId了吧。不过大家要注意一点这里的SessionIDManager 在操作cookie做了一些数据验证处理,如果在特殊情况需要自定义验证规则我们可以自己来实现ISessionIDManager接口。这里我们可以看到第一次请求是没有sessionid的,所以sessionStateItem = this.GetSessionStateItem();这句代码不会执行,sessionStateItem默认为true,但是第二次请求时有sessionid这句代码就会执行。GetSessionStateItem()的实现这里我们就忽略了吧,这个方法设置一个SessionStateStoreData的实例 this._rqItem ,如果 this._rqItem为null则返回false。一般我们的Session都是可读写的。GetSessionStateItem方法主要是调用  this._rqItem = this._store.GetItemExclusive(this._rqContext, this._rqId, out flag2, out span, out this._rqLockId, out this._rqActionFlags);

    现在我们回到CompleteAcquireState方法中来:

      if (flag)
                {
                    SessionStateUtility.AddDelayedHttpSessionStateToContext(this._rqContext, this);
                    this._rqSessionState = s_delayedSessionState;
                }
                else
                {
                    this.InitStateStoreItem(true); //SessionStateUtility.AddHttpSessionStateToContext(this._rqContext, this._rqSessionState);
                }

    这里是flag默认是false,里面具体判断就不说,InitStateStoreItem方法主要代码:

    if (this._rqItem == null)
                {
                    this._rqItem = this._store.CreateNewStoreData(this._rqContext, s_timeout);
                }

    this._rqSessionItems = this._rqItem.Items;

       this._rqSessionState = new HttpSessionStateContainer(this, this._rqId, this._rqSessionItems, this._rqStaticObjects, this._rqItem.Timeout, this._rqIsNewSession, s_configCookieless, s_configMode, this._rqReadonly);
                SessionStateUtility.AddHttpSessionStateToContext(this._rqContext, this._rqSessionState);

    这里InProcSessionStateStore 的CreateNewStoreData方法实际就是调用SessionStateUtility.CreateLegitStoreData:

     internal static SessionStateStoreData CreateLegitStoreData(HttpContext context, ISessionStateItemCollection sessionItems, HttpStaticObjectsCollection staticObjects, int timeout)
    {
        if (sessionItems == null)
        {
            sessionItems = new SessionStateItemCollection();
        }
        if ((staticObjects == null) && (context != null))
        {
            staticObjects = GetSessionStaticObjects(context);
        }
        return new SessionStateStoreData(sessionItems, staticObjects, timeout);
    }

    其中SessionStateItemCollection的定义如下:

    public sealed class SessionStateItemCollection : NameObjectCollectionBase, ISessionStateItemCollection, ICollection, IEnumerable

    这里创建了一个  HttpSessionStateContainer实例。我想大家到这里就应该明白我们的Session实际上就是一个HttpSessionStateContainer实例

    好现在我来看 Session.SessionID这个是怎么实现的
    public string SessionID
    {
        get
        {
            if (this._id == null)
            {
                this._id = this._stateModule.DelayedGetSessionId();
            }
            return this._id;
        }
    }

    而SessionStateModule的DelayedGetSessionId方法实现如下:

    internal string DelayedGetSessionId()
    {
        this.ChangeImpersonation(this._rqContext, false);
        try
        {
            this._rqId = this._idManager.GetSessionID(this._rqContext);
            if (this._rqId == null)
            {
                this.CreateSessionId();
            }

        }
        finally
        {
            this.RestoreImpersonation();
        }
        return this._rqId;
    }
    这里的CreateSessionId具体是怎么创建我就不说了吧,知道它是真正创建sessionid的就可以。而session的实际操作都是在ISessionStateItemCollection里面如HttpSessionStateContainer的Add方法:

    public void Add(string name, object value)
    {
        this._sessionItems[name] = value;
    }

    而这里的_sessionItems实际上是this._rqItem.Items,本来想忽略_rqItem的创建,看来这个实例比较强啊。

     this._rqItem = this._store.GetItemExclusive(this._rqContext, this._rqId, out flag2, out span, out this._rqLockId, out this._rqActionFlags);
            if ((((this._rqItem == null) && !flag2) && (this._rqId != null)) && ((s_configCookieless != HttpCookieMode.UseUri) || !s_configRegenerateExpiredSessionId))
            {
                this.CreateUninitializedSessionState();
                this._rqItem = this._store.GetItemExclusive(this._rqContext, this._rqId, out flag2, out span, out this._rqLockId, out this._rqActionFlags);
            }

    这里的CreateUninitializedSessionState方法实际就是调用this._store.CreateUninitializedItem(this._rqContext, this._rqId, s_timeout);

    我们前面知道this._store这个可以取很多实例的,是SessionStateStoreProviderBase类型,这里我们也已默认的 InProcSessionStateStore(继承SessionStateStoreProviderBase)来说说吧,相关方法:

    private SessionStateStoreData DoGet(HttpContext context, string id, bool exclusive, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actionFlags)
    {
        bool flag;
        string key = this.CreateSessionStateCacheKey(id);
        InProcSessionState state = (InProcSessionState) HttpRuntime.CacheInternal.Get(key);
        if (state == null)
        {
            return null;
        }
      ......
        return SessionStateUtility.CreateLegitStoreData(context, state._sessionItems, state._staticObjects, state._timeout);
    }
    
    public override void CreateUninitializedItem(HttpContext context, string id, int timeout)
    {
        string key = this.CreateSessionStateCacheKey(id);
        SessionIDManager.CheckIdLength(id, true);
        InProcSessionState state = new InProcSessionState(null, null, timeout, false, DateTime.MinValue, NewLockCookie, 1);
        try
        {
        }
        finally
        {
            if (HttpRuntime.CacheInternal.UtcAdd(key, state, null, Cache.NoAbsoluteExpiration, new TimeSpan(0, timeout, 0), CacheItemPriority.NotRemovable, this._callback) == null)
            {
                PerfCounters.IncrementCounter(AppPerfCounter.SESSIONS_TOTAL);
                PerfCounters.IncrementCounter(AppPerfCounter.SESSIONS_ACTIVE);
            }
        }
    }

    现在我们终于明白一个Sessionid对应一个SessionStateStoreData,所以它能区分不同的用户请求,这里的id就是我们前面的this._rqId了。

    现在我们也总结一下吧,我们的HttpContext的Session属性实际上是一个HttpSessionStateContainer实例(HttpSessionStateContainer继承IHttpSessionState),而它数据成员都是保存在ISessionStateItemCollection实例中,每一次http请求我们都会去获取它的Sessionid,第一次请求sessionid问null,我们没有对应的SessionStateStoreData数据,这时我们在SessionStateModule的 InitStateStoreItem方法调用SessionStateStoreProviderBase的CreateNewStoreData方法来创建一个SessionStateStoreData实例,其中该实例有一个成员变量类型是ISessionStateItemCollection用来保存用户session的数据。同一个用户第二次请求我们能获取到它的sessionid,默认也能获取到SessionStateStoreData实例(session过期则取不到)。一个用户对应一个SessionStateStoreData,每个SessionStateStoreData里面有一个ISessionStateItemCollection实例用来保存用户数据,至于sessionid也就是用户身份的区别依赖于ISessionIDManager的实现。


    前几天有人问我session过期处理流程是怎么样的。这里以InProcSessionStateStore为列来简单说明一下:

    InProcSessionStateStore中有CreateUninitializedItem方法和SetAndReleaseItemExclusive方法,分别有HttpRuntime.CacheInternal.UtcAdd(key, state, null, Cache.NoAbsoluteExpiration, new TimeSpan(0, timeout, 0), CacheItemPriority.NotRemovable, this._callback)
    和 cacheInternal.UtcInsert(key, state2, null, Cache.NoAbsoluteExpiration, new TimeSpan(0, state2._timeout, 0), CacheItemPriority.NotRemovable, this._callback);的方法调用

    其中this._callback的赋值语句在Initialize方法中
    public override void Initialize(string name, NameValueCollection config)
        {
            if (string.IsNullOrEmpty(name))
            {
                name = "InProc Session State Provider";
            }
            base.Initialize(name, config);
            this._callback = new CacheItemRemovedCallback(this.OnCacheItemRemoved);
        }
     public void OnCacheItemRemoved(string key, object value, CacheItemRemovedReason reason)
        {
            PerfCounters.DecrementCounter(AppPerfCounter.SESSIONS_ACTIVE);
            InProcSessionState state = (InProcSessionState) value;
            if (((state._flags & 2) == 0) && ((state._flags & 1) == 0))
            {
                switch (reason)
                {
                    case CacheItemRemovedReason.Removed:
                        PerfCounters.IncrementCounter(AppPerfCounter.SESSIONS_ABANDONED);
                        break;

                    case CacheItemRemovedReason.Expired:
                        PerfCounters.IncrementCounter(AppPerfCounter.SESSIONS_TIMED_OUT);
                        break;
                }
                if (this._expireCallback != null)
                {
                    string id = key.Substring(CACHEKEYPREFIXLENGTH);
                    this._expireCallback(id, SessionStateUtility.CreateLegitStoreData(null, state._sessionItems, state._staticObjects, state._timeout));
                }
            }
        }
    现在我们来看看 this._expireCallback是什么东东

     public override bool SetItemExpireCallback(SessionStateItemExpireCallback expireCallback)
        {
            this._expireCallback = expireCallback;
            return true;
        }

    SetItemExpireCallback则在SessionStateModule类中调用

      public event EventHandler End {
                add {
                    lock(_onEndTarget) {
                        if (_store != null && _onEndTarget.SessionEndEventHandlerCount == 0) {
                            _supportSessionExpiry = _store.SetItemExpireCallback(
                                    new SessionStateItemExpireCallback(_onEndTarget.RaiseSessionOnEnd));
                        }
                        ++_onEndTarget.SessionEndEventHandlerCount;
                    }
                }
                remove {
                    lock(_onEndTarget) {
                        --_onEndTarget.SessionEndEventHandlerCount;
                        //
                        if (_store != null && _onEndTarget.SessionEndEventHandlerCount == 0) {
                            _store.SetItemExpireCallback(null);
                            _supportSessionExpiry = false;
                        }
                    }
                }
            }

    其中SessionOnEndTarget的定义如下:

     class SessionOnEndTarget {
            internal int _sessionEndEventHandlerCount;
    
            internal SessionOnEndTarget() { 
            }
     
            internal int SessionEndEventHandlerCount { 
                get {
                    return _sessionEndEventHandlerCount; 
                }
                set {
                    _sessionEndEventHandlerCount = value;
                } 
            }
     
            internal void RaiseOnEnd(HttpSessionState sessionState) { 
                Debug.Trace("SessionOnEnd", "Firing OnSessionEnd for " + sessionState.SessionID);
     
                if (_sessionEndEventHandlerCount > 0) {
                    HttpApplicationFactory.EndSession(sessionState, this, EventArgs.Empty);
                }
            } 
    
            internal void RaiseSessionOnEnd(String id, SessionStateStoreData item) { 
                HttpSessionStateContainer sessionStateContainer = new HttpSessionStateContainer( 
                        id,
                        item.Items, 
                        item.StaticObjects,
                        item.Timeout,
                        false,
                        SessionStateModule.s_configCookieless, 
                        SessionStateModule.s_configMode,
                        true); 
     
                HttpSessionState    sessionState = new HttpSessionState(sessionStateContainer);
     
                if (HttpRuntime.ShutdownInProgress) {
                    // call directly when shutting down
                    RaiseOnEnd(sessionState);
                } 
                else {
                    // post via thread pool 
                    SessionOnEndTargetWorkItem workItem = new SessionOnEndTargetWorkItem(this, sessionState); 
                    WorkItem.PostInternal(new WorkItemCallback(workItem.RaiseOnEndCallback));
                } 
            }
    
        }
    View Code

    主要调用   HttpApplicationFactory.EndSession(sessionState, this, EventArgs.Empty);

    其中主要的调用过程如下:

    internal static void EndSession(HttpSessionState session, object eventSource, EventArgs eventArgs)
    {
        _theApplicationFactory.FireSessionOnEnd(session, eventSource, eventArgs);
    }
    private void FireSessionOnEnd(HttpSessionState session, object eventSource, EventArgs eventArgs)
    {
        if (this._sessionOnEndMethod != null)
        {
            HttpApplication specialApplicationInstance = this.GetSpecialApplicationInstance();
            if (AspCompatApplicationStep.AnyStaObjectsInSessionState(session) || HttpRuntime.ApartmentThreading)
            {
                AspCompatSessionOnEndHelper source = new AspCompatSessionOnEndHelper(specialApplicationInstance, session, eventSource, eventArgs);
                AspCompatApplicationStep.RaiseAspCompatEvent(null, specialApplicationInstance, session.SessionID, this._sessionOnEndEventHandlerAspCompatHelper, source, EventArgs.Empty);
            }
            else
            {
                specialApplicationInstance.ProcessSpecialRequest(null, this._sessionOnEndMethod, this._sessionOnEndParamCount, eventSource, eventArgs, session);
            }
            this.RecycleSpecialApplicationInstance(specialApplicationInstance);
        }
    }
     internal HttpApplicationFactory()
        {
            this._sessionOnEndEventHandlerAspCompatHelper = new EventHandler(this.SessionOnEndEventHandlerAspCompatHelper);
        }
       private void SessionOnEndEventHandlerAspCompatHelper(object eventSource, EventArgs eventArgs)
        {
            AspCompatSessionOnEndHelper helper = (AspCompatSessionOnEndHelper) eventSource;
            helper.Application.ProcessSpecialRequest(null, this._sessionOnEndMethod, this._sessionOnEndParamCount, helper.Source, helper.Args, helper.Session);
        }
    
        private bool ReflectOnMethodInfoIfItLooksLikeEventHandler(MethodInfo m) {
                if (m.ReturnType != typeof(void))
                    return false;
     
                // has to have either no args or two args (object, eventargs)
                ParameterInfo[] parameters = m.GetParameters();
     
                switch (parameters.Length) {
                    case 0:
                        // ok
                        break;
                    case 2:
                        // param 0 must be object
                        if (parameters[0].ParameterType != typeof(System.Object))
                            return false;
                        // param 1 must be eventargs
                        if (parameters[1].ParameterType != typeof(System.EventArgs) &&
                            !parameters[1].ParameterType.IsSubclassOf(typeof(System.EventArgs)))
                            return false;
                        // ok
                        break;
     
                    default:
                        return false;
                }

                // check the name (has to have _ not as first or last char)
                String name = m.Name;
                int j = name.IndexOf('_');
                if (j <= 0 || j > name.Length-1)
                    return false;
     
                // special pseudo-events
                if (StringUtil.EqualsIgnoreCase(name, "Application_OnStart") ||
                    StringUtil.EqualsIgnoreCase(name, "Application_Start")) {
                    _onStartMethod = m;
                    _onStartParamCount = parameters.Length;
                }
                else if (StringUtil.EqualsIgnoreCase(name, "Application_OnEnd") ||
                         StringUtil.EqualsIgnoreCase(name, "Application_End")) {
                    _onEndMethod = m;
                    _onEndParamCount = parameters.Length;
                }
                else if (StringUtil.EqualsIgnoreCase(name, "Session_OnEnd") ||
                         StringUtil.EqualsIgnoreCase(name, "Session_End")) {
                    _sessionOnEndMethod = m;
                    _sessionOnEndParamCount = parameters.Length;
                }
     
                return true;
            }

     那么这里的 public event EventHandler End 事件又是在哪里触发的了:

    System.Web.Hosting.PipelineRuntime:
    InitializeApplication(System.IntPtr appContext)
    {
     ................
     if (!HttpRuntime.HostingInitFailed) {
            //
            //  On IIS7, application initialization does not provide an http context.  Theoretically,
            //  no one should be using the context during application initialization, but people do. 
            //  Create a dummy context that is used during application initialization
            //  to prevent breakage (ISAPI mode always provides a context) 
            // 
            HttpWorkerRequest initWorkerRequest = new SimpleWorkerRequest("" /*page*/,
                                                                          "" /*query*/, 
                                                                          new StringWriter(CultureInfo.InvariantCulture));
            MimeMapping.SetIntegratedApplicationContext(appContext);
            HttpContext initHttpContext = new HttpContext(initWorkerRequest);
            app = HttpApplicationFactory.GetPipelineApplicationInstance(appContext, initHttpContext); 
        }
        ..........
    }
    HttpApplicationFactory:
       internal static HttpApplication GetPipelineApplicationInstance(IntPtr appContext, HttpContext context) {
                _theApplicationFactory.EnsureInited(); 
                return _theApplicationFactory.GetSpecialApplicationInstance(appContext, context); 
            }
    
           private HttpApplication GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) {
            HttpApplication app = null; 
    
            lock (_specialFreeList) {
                if (_numFreeSpecialAppInstances > 0) { 
                    app = (HttpApplication)_specialFreeList.Pop();
                    _numFreeSpecialAppInstances--;
                }
            } 
    
            if (app == null) { 
                // 
                //  Put the context on the thread, to make it available to anyone calling
                //  HttpContext.Current from the HttpApplication constructor or module Init 
                //
                using (new DisposableHttpContextWrapper(context)) {
                    // If ran out of instances, create a new one
                    app = (HttpApplication)HttpRuntime.CreateNonPublicInstance(_theApplicationType); 
    
                    using (new ApplicationImpersonationContext()) { 
                        app.InitSpecial(_state, _eventHandlerMethods, appContext, context); 
                    }
                } 
            }
    
            return app;
        } 
            
    HttpApplication:
      internal void InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) {
            .......
            Debug.Trace("PipelineRuntime", "InitSpecial for " + appContext.ToString() + "\n");
            RegisterEventSubscriptionsWithIIS(appContext, context, handlers); 
            ......
            } 
     private void RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) {
                RequestNotification requestNotifications;
                RequestNotification postRequestNotifications;
             .........
                if (handlers != null) {
                    HookupEventHandlersForApplicationAndModules(handlers); 
                }
    
                // 1643363: Breaking Change: ASP.Net v2.0: Application_OnStart is called after Module.Init (Integarted mode)
                HttpApplicationFactory.EnsureAppStartCalledForIntegratedMode(context, this); 
    
                // Call Init on HttpApplication derived class ("global.asax") 
                // and process event subscriptions before processing other modules. 
                // Doing this now prevents clearing any events that may
                // have been added to event handlers during instantiation of this instance. 
                // NOTE:  If "global.asax" has a constructor which hooks up event handlers,
                // then they were added to the event handler lists but have not been registered with IIS yet,
                // so we MUST call ProcessEventSubscriptions on it first, before the other modules.
                _currentModuleCollectionKey = HttpApplicationFactory.applicationFileName; 
    
             ..........
            }
          private void HookupEventHandlersForApplicationAndModules(MethodInfo[] handlers) {
            ........
            try {
                addMethod.Invoke(target, new Object[1]{handlerDelegate}); 
            } 
            catch {
                if (HttpRuntime.UseIntegratedPipeline) { 
                    throw;
                }
            }
             .......
        } 
          private void ReflectOnApplicationType() {
                ArrayList handlers = new ArrayList();
                MethodInfo[] methods;
     
                Debug.Trace("PipelineRuntime", "ReflectOnApplicationType");
     
                // get this class methods 
                methods = _theApplicationType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
                foreach (MethodInfo m in methods) { 
                    if (ReflectOnMethodInfoIfItLooksLikeEventHandler(m))
                        handlers.Add(m);
                }
     
                // get base class private methods (GetMethods would not return those)
                Type baseType = _theApplicationType.BaseType; 
                if (baseType != null && baseType != typeof(HttpApplication)) { 
                    methods = baseType.GetMethods(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
                    foreach (MethodInfo m in methods) { 
                        if (m.IsPrivate && ReflectOnMethodInfoIfItLooksLikeEventHandler(m))
                            handlers.Add(m);
                    }
                } 
    
                // remember as an array 
                _eventHandlerMethods = new MethodInfo[handlers.Count]; 
                for (int i = 0; i < _eventHandlerMethods.Length; i++)
                    _eventHandlerMethods[i] = (MethodInfo)handlers[i]; 
            }

    我想大家看到这里就知道调用End事件是HttpApplicationHookupEventHandlersForApplicationAndModules方法中的 addMethod.Invoke(target, new Object[1]{handlerDelegate}); 这句实现的,触发来源于PipelineRuntimeInitializeApplication方法,至于PipelineRuntime.InitializeApplication 我就不多说了,不过HookupEventHandlersForApplicationAndModules 中的handlers是在ReflectOnApplicationType中赋值,说白就是HTTApplication中自定义事件。

    同样这里我们也说说Session start方法:

    在SessionStateModule的Init-〉InitModuleFromConfig-〉    app.AddOnAcquireRequestStateAsync(new BeginEventHandler(this.BeginAcquireState), new EndEventHandler(this.EndAcquireState)); 中的BeginAcquireState-〉

             if (sessionStateItem)
            {
                this.CompleteAcquireState();
                this._rqAr.Complete(true, null, null);
            }

       在CompleteAcquireState()有

        if (this._rqIsNewSession)
            {
                this.OnStart(EventArgs.Empty);
            }

    private void OnStart(EventArgs e)
    {
        this.RaiseOnStart(e);
    }
    
     private void RaiseOnStart(EventArgs e)
    {
        if (this._sessionStartEventHandler != null)
        {
            if (HttpRuntime.ApartmentThreading || this._rqContext.InAspCompatMode)
            {
                AspCompatApplicationStep.RaiseAspCompatEvent(this._rqContext, this._rqContext.ApplicationInstance, null, this._sessionStartEventHandler, this, e);
            }
            else
            {
                if (HttpContext.Current == null)
                {
                    DisposableHttpContextWrapper.SwitchContext(this._rqContext);
                }
                this._sessionStartEventHandler(this, e);
            }
        }
    }
    View Code

    这里的_sessionStartEventHandler默认就是我们的Global中的Session_Start方法。

    -----------------2017-2-11 最加

     这里说一下Session_Start的方法调用的确与这里的this._rqIsNewSession 有关 但是该值的赋值实在CompleteAcquireState方法里面:

        void CompleteAcquireState() {
                Debug.Trace("SessionStateModuleOnAcquireState", "Item retrieved=" + (_rqItem != null).ToString(CultureInfo.InvariantCulture));
                bool delayInitStateStoreItem = false;
    
                Debug.Assert(!(s_allowDelayedStateStoreItemCreation && s_configRegenerateExpiredSessionId),
                    "!(s_allowDelayedStateStoreItemCreation && s_configRegenerateExpiredSessionId)");
    
                try {
                    if (_rqItem != null) {
                        _rqSessionStateNotFound = false;
    
                        if ((_rqActionFlags & SessionStateActions.InitializeItem) != 0) {
                            Debug.Trace("SessionStateModuleOnAcquireState", "Initialize an uninit item");
                            _rqIsNewSession = true;
                        }
                        else {
                            _rqIsNewSession = false;
                        }
                    }
                    else {
                        _rqIsNewSession = true;
                        _rqSessionStateNotFound = true;
    
                        if (s_allowDelayedStateStoreItemCreation) {
                            Debug.Trace("SessionStateModuleOnAcquireState", "Delay creating new session state");
                            delayInitStateStoreItem = true;
                        }
    
                        // We couldn't find the session state.
                        if (!_rqIdNew &&                            // If the request has a session id, that means the session state has expired
                            s_configRegenerateExpiredSessionId &&   // And we're asked to regenerate expired session
                            _rqSupportSessionIdReissue) {           // And this request support session id reissue
    
                            // We will generate a new session id for this expired session state
                            bool redirected = CreateSessionId();
    
                            Debug.Trace("SessionStateModuleOnAcquireState", "Complete re-creating new id; redirected=" + redirected);
    
                            if (redirected) {
                                Debug.Trace("SessionStateModuleOnAcquireState", "Will redirect because we've reissued a new id and it's cookieless");
                                CreateUninitializedSessionState();
                                return;
                            }
                        }
                    }
    
                    if (delayInitStateStoreItem) {
                        _rqSessionState = s_delayedSessionState;
                    }
                    else {
                        InitStateStoreItem(true);
                    }
    
                    // Set session state module
                    SessionStateUtility.AddHttpSessionStateModuleToContext(_rqContext, this, delayInitStateStoreItem);
    
                    if (_rqIsNewSession) {
                        Debug.Trace("SessionStateModuleOnAcquireState", "Calling OnStart");
                        OnStart(EventArgs.Empty);
                    }
                }
                finally {
                    if (EtwTrace.IsTraceEnabled(EtwTraceLevel.Information, EtwTraceFlags.AppSvc)) EtwTrace.Trace(EtwTraceType.ETW_TYPE_SESSION_DATA_END, _rqContext.WorkerRequest);
                }
    
    #if DBG
                if (_rqIsNewSession) {
                    if (_rqId == null) {
                        Debug.Assert(s_allowInProcOptimization, "s_allowInProcOptimization");
                        Debug.Trace("SessionStateModuleOnAcquireState", "New session: session id reading is delayed"+
                                    "\nReturning from SessionStateModule::OnAcquireState");
                    }
                    else {
                        Debug.Trace("SessionStateModuleOnAcquireState", "New session: SessionId= " + _rqId +
                                    "\nReturning from SessionStateModule::OnAcquireState");
                    }
    
                }
                else {
                    Debug.Trace("SessionStateModuleOnAcquireState", "Retrieved old session, SessionId= " + _rqId +
                                "\nReturning from SessionStateModule::OnAcquireState");
    
                }
    #endif
            }
    View Code

    _rqItem 对象就是我们的SessionStateStoreData 实例,一般浏览器第一次请求这个值为null (客服端没有对应的ASP.NET_SessionId) 所以 _rqIsNewSession为true。但是最近遇到这样一个问题,访问一个web站点,该站点配置了2个端口,比如192.168.1.100:8081 和192.168.1.100:8082,先访问8081 然后在访问8082,在第一次访问8081的时候_rqIsNewSession为true,那么访问8081后第一次访问8082的时候发现_rqIsNewSession还是true。 于是就查看源码哦。 发现与_rqActionFlags参数有关。该参数在调用SessionStateStoreProviderBase的GetItemExclusive或GetItem方法被赋值。 在InProcSessionStateStore里面退码实际上调用:

      SessionStateStoreData DoGet(HttpContext context, 
                                            String id,
                                            bool exclusive,
                                            out bool locked,
                                            out TimeSpan lockAge, 
                                            out object lockId,
                                            out SessionStateActions actionFlags) {
                string  key = CreateSessionStateCacheKey(id);
    
                // Set default return values
                locked = false;
                lockId = null;
                lockAge = TimeSpan.Zero;
                actionFlags = 0;
    
                // Not technically necessary for InProc, but we do it to be consistent
                // with SQL provider
                SessionIDManager.CheckIdLength(id, true /* throwOnFail */);
    
                InProcSessionState state = (InProcSessionState) HttpRuntime.CacheInternal.Get(key);
                if (state != null) {
                    bool    lockedByOther;       // True if the state is locked by another session
                    int initialFlags;
    
                    initialFlags = (int)state._flags;
                    if ((initialFlags & (int)SessionStateItemFlags.Uninitialized) != 0) {
                        // It is an uninitialized item.  We have to remove that flag.
                        // We only allow one request to do that.
                        // For details, see inline doc for SessionStateItemFlags.Uninitialized flag.
    
                        // If initialFlags != return value of CompareExchange, it means another request has
                        // removed the flag.
    
                        Debug.Trace("SessionStateClientSet", "Removing the Uninit flag for item; key = " + key);
                        if (initialFlags == Interlocked.CompareExchange(
                                                ref state._flags, 
                                                initialFlags & (~((int)SessionStateItemFlags.Uninitialized)), 
                                                initialFlags)) {
                            actionFlags = SessionStateActions.InitializeItem;
                        }
                    }
    
                    if (exclusive) {
                        lockedByOther = true;
                        
                        // If unlocked, use a spinlock to test and lock the state.
                        if (!state._locked) {
                            state._spinLock.AcquireWriterLock();
                            try {
                                if (!state._locked) {
                                    lockedByOther = false;
                                    state._locked = true;
                                    state._utcLockDate = DateTime.UtcNow;
                                    state._lockCookie++;
                                }
                                lockId = state._lockCookie;
                            }
                            finally {
                                state._spinLock.ReleaseWriterLock();
                            }
                        }
                        else {
                            // It's already locked by another request.  Return the lockCookie to caller.
                            lockId = state._lockCookie;
                        }
    
                    }
                    else {
                        state._spinLock.AcquireReaderLock();
                        try {
                            lockedByOther = state._locked;
                            lockId = state._lockCookie;
                        }
                        finally {
                            state._spinLock.ReleaseReaderLock();
                        }
                    }
                    
                    if (lockedByOther) {
                        // Item found, but locked
                        locked = true;
                        lockAge = DateTime.UtcNow - state._utcLockDate;
                        return null;
                    }
                    else {
                        return SessionStateUtility.CreateLegitStoreData(context, state._sessionItems,
                                                    state._staticObjects, state._timeout);
                    }
                }
    
                // Not found
                return null;
            }
    View Code

     而我真是的codey用的是 RedisSessionStateProvider ,默认取值 actions = SessionStateActions.None;也就是说session 对应的cookieID 不存在或者session 过期后,会触发我们的Session_Start方法。 

  • 相关阅读:
    日报9.4
    日报9.3
    低级错误整理
    树状数组求逆序对 笔记与思路整理
    st表、树状数组与线段树 笔记与思路整理
    Luogu P1098 字符串的展开
    Luogu P1816 忠诚
    jmeter cookie管理器 使用方法---新手学习记录1
    kali nessus 安装插件失败解决方法
    https tomcat 证书搭建
  • 原文地址:https://www.cnblogs.com/majiang/p/2793389.html
Copyright © 2020-2023  润新知