我们还是简单的来复习一下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; }