为了理解为什么一个输出缓存的请求不会创建一个会话,你需要稍微了解一下ASP.NET Application Life Cycle,即事件。基础是定义的一系列HttpApplication events,可以在每个请求期间触发。这些事件通常通过HttpModule 实现或Global.asax file 使用事件处理程序订阅。
并非每个应用程序事件都会在每个请求时触发,但触发的事件将始终按定义的顺序触发。在 IIS 7 上触发的事件顺序
1. BeginRequest
2. AuthenticateRequest
PostAuthenticateRequest
3. AuthorizeRequest
PostAuthorizeRequest
4. ResolveRequestCache
PostResolveRequestCache
5. MapRequestHandler (Integrated Mode Only)
PostMapRequestHandler
6. AcquireRequestState
PostAcquireRequestState
7. PreRequestHandlerExecute
<--- the IHttpHandler.ProcessRequest() method is called here
PostRequestHandlerExecute
8. ReleaseRequestState
PostReleaseRequestState
9. UpdateRequestCache
PostUpdateRequestCache
10. LogRequest (Integrated Mode Only)
PostLogRequest (Integrated Mode Only)
11. EndRequest
12. PreSendRequestHeaders
13. PreSendRequestContent
IHttpModule 的工作原理
IHttpModule 界面如下所示:
public interface IHttpModule
{
void Init(HttpApplication context);
void Dispose();
}
Init() 方法用于将事件处理程序订阅到应用程序事件,当应用程序完成后,Dispose() 方法会在模块之后进行清理。
ASP.NET 会话如何工作
System.Web 定义了一个名为System.Web.SessionState.SessionStateModule 的IHttpModule 实现。如果会话未在 web.config 中禁用,则会连接以下事件处理程序:
// app is the current HttpApplication ;
app.AddOnAcquireRequestStateAsync(this.BeginAcquireState, this.EndAcquireState);
app.ReleaseRequestState += this.OnReleaseState;
app.EndRequest += this.OnEndRequest;
会话的工作方式取决于正在运行的会话模式,但要了解的关键是会话是在 SessionStateModule.BeginAcquireState 方法中检索和创建的,并且该方法与 AcquireRequestState 事件异步连接。
输出缓存的工作原理
System.Web 定义了一个名为System.Web.Caching.OutputCacheModule 的内部IHttpModule 实现。 Init() 方法看起来像:
void IHttpModule.Init(HttpApplication app)
{
if (RuntimeConfig.GetAppConfig().OutputCache.EnableOutputCache)
{
app.ResolveRequestCache += new EventHandler(this.OnEnter);
app.UpdateRequestCache += new EventHandler(this.OnLeave);
}
}
OnEnter 方法评估缓存参数并查找与请求匹配的缓存响应。 OnLeave 方法缓存可缓存的响应。您需要知道的一件事是,如果OnEnter 方法成功检索缓存响应,它会在HttpApplication 实例上调用CompleteRequest() 方法。此方法的文档中写道:“导致 ASP.NET 绕过 HTTP 执行管道链中的所有事件和过滤,并直接执行 EndRequest 事件”。因此,在调用CompleteRequest() 和EndRequest() 事件之间发生的任何事件都将被跳过。
如果页面被缓存,为什么不创建会话?
-
SessionStateModule 订阅 SessionStateModule.BeginAcquireState 到应用程序的 AcquireRequestState 事件,因此这是创建和检索会话的地方。
-
OutputCacheModule 订阅OutputCacheModule.OnEnter 到应用程序的ResolveRequestCache 事件,因此这是检索缓存响应和调用CompleteRequest() 的位置。这意味着对于缓存的请求将永远不会触发以下事件:MapRequestHandler/PostMapRequestHandler; AcquireRequestState/PostAcquireRequestState; PreRequestHandlerExecute; IHttpHandler.ProcessRequest; PostRequestHandlerExecute; ReleaseRequestState/PostReleaseRequestState; UpdateRequestCache/PostUpdateRequestCache;还有,LogRequest/PostLogRequest。
- 如果检索到缓存的响应,则调用
HttpApplication.CompleteRequest(),不会触发 AcquireRequestState 事件,也不会创建会话。
该怎么办?
禁用页面上的输出缓存以支持控件是否是可行的解决方案取决于:(1)使用会话的代码是在控件中还是在页面中; (2) 你的应用实例需要能够支持多少负载; (3) 应用程序或其支持基础设施中存在哪些其他缓存机制。根据您的基础架构,可能还有其他因素需要考虑。例如,根据会话状态模式和环境是否负载平衡,ASP.NET 会话的执行和行为可能非常不同。此外,如果您碰巧正在运行像 Varnish 之类的 HTTP 加速器缓存(例如),在先前输出缓存的 ASP.NET 分页上启用会话可能会将行为从忽略会话更改为附加属于不同用户的陈旧会话.这只是一个假设示例,但重点是在做出此类决定时可能需要考虑其他因素。