我們還是簡單的來復習一下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)); } } }
主要調用 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事件是HttpApplication類HookupEventHandlersForApplicationAndModules方法中的 addMethod.Invoke(target, new Object[1]{handlerDelegate}); 這句實現的,觸發來源於PipelineRuntime的InitializeApplication方法,至於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); } } }
這里的_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 }
_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; }
而我真是的codey用的是 RedisSessionStateProvider ,默認取值 actions = SessionStateActions.None;也就是說session 對應的cookieID 不存在或者session 過期后,會觸發我們的Session_Start方法。