技术开发 频道

ASP.NET 中 Session 实现原理浅析


  在 .NET 安装目录 Config 子目录下的 machine.config 定义了全局性的配置信息,而 HttpApplication 就是使用其中 system.web 一节的配置信息进行初始化的。

以下内容为程序代码:

<system.web>
<httpModules>
<add name="OutputCache" type="System.Web.Caching.OutputCacheModule" />
<add name="Session" type="System.Web.SessionState.SessionStateModule" />
<add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule" />
<add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule" />
<add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule" />
<add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule" />
<add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule" />
<add name="ErrorHandlerModule" type="System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
</httpModules>
</system.web>

  httpModules 节点指定了 HttpApplication 需要初始化的模块列表,而在前面提到的 HttpApplication.InitModules 函数正式根据此列表进行初始化的。

以下内容为程序代码:

private void HttpApplication.InitModules()
{
HttpModulesConfiguration cfgModules = ((HttpModulesConfiguration) HttpContext.GetAppConfig("system.web/httpModules"[img]/images/wink.gif[/img]);

if (cfgModules == null)
{
throw new HttpException(HttpRuntime.FormatResourceString("Missing_modules_config"[img]/images/wink.gif[/img]);
}
_moduleCollection = cfgModules.CreateModules();

for(int i = 0; i < _moduleCollection.Count; i++)
{
_moduleCollection[i].Init(this);
}

GlobalizationConfig cfgGlobal = ((GlobalizationConfig) HttpContext.GetAppConfig("system.web/globalization"[img]/images/wink.gif[/img]);
if (cfgGlobal != null)
{
_appLevelCulture = cfgGlobal.Culture;
_appLevelUICulture = cfgGlobal.UICulture;
}
}

  Session 节点对于的 System.Web.SessionState.SessionStateModule 对象将被 HttpModulesConfiguration.CreateModules 方法构造,并调用其 Init 函数初始化。SessionStateModule 类实际上就是负责管理并创建会话,用户完全可以自行创建一个实现 IHttpModule 接口的类,实现会话的控制,如实现支持集群的状态同步等等。
  SessionStateModule.Init 方法主要负责 machine.config 文件中的 sessionState 配置,调用 SessionStateModule.InitModuleFromConfig 方法建立相应的会话管理器。

以下内容为程序代码:

<system.web>
<sessionState mode="InProc"
stateConnectionString="tcpip=127.0.0.1:42424"
stateNetworkTimeout="10"
sqlConnectionString="data source=127.0.0.1;Integrated Security=SSPI"
cookieless="false"
timeout="20" />
</system.web>

  sessionState 的使用方法请参加 MSDN 中相关介绍和 INFO: ASP.NET State Management Overview。关于不同 mode 的会话管理的话题我们后面再讨论,先继续来看会话的建立过程。

  在从 machine.config 文件中读取配置信息后,InitModuleFromConfig 方法会向 HttpApplication 实例注册几个会话管理事件处理函数,负责在应用程序合适的情况下维护会话状态。

以下内容为程序代码:

private void SessionStateModule.InitModuleFromConfig(HttpApplication app,
SessionStateSectionHandler.Config config, bool configInit)
{
// 处理不使用 Cookie 的情况
//...

app.AddOnAcquireRequestStateAsync(new BeginEventHandler(this.BeginAcquireState),
new EndEventHandler(this.EndAcquireState));
app.ReleaseRequestState += new EventHandler(this.OnReleaseState);
app.EndRequest += new EventHandler(this.OnEndRequest);

// 创建会话管理器
//...
}

  BeginAcquireState 和 EndAcquireState 作为一个异步处理器注册到 HttpApplication._acquireRequestStateEventHandlerAsync 字段上;OnReleaseState 则负责在合适的时候清理会话状态;OnEndRequest 则是 OnReleaseState 的一个包装,负责较为复杂的请求结束处理。前面提到的 HttpApplication.InitInternal 函数,在完成了初始化工作后,会将上述这些事件处理器,加入到一个执行队列中,由应用程序在合适的时候,使用流水线机制进行调用,最大化处理效率。有关 ASP.NET 中流水线事件模型的相关介绍,请参考 HTTP PIPELINES
Securely Implement Request Processing, Filtering, and Content Redirection with HTTP Pipelines in ASP.NET 一文中 The Pipeline Event Model 小节。

  知道了会话建立的调用流程再来看会话的实现就比较简单了,SessionStateModule.BeginAcquireState 被 HttpApplication 实例在合适的时候调用,处理各种会话的复杂情况后,使用 SessionStateModule.CompleteAcquireState 函数完成实际的会话建立工作,并将封装会话的 HttpSessionState 对象以 "AspSession" 为 key 加入到 HttpContext 的哈希表中,也就是前面提到的 HttpContext.Context 的由来。而 SessionStateModule.OnReleaseState 则从 HttpContext 中删除 "AspSession" 为 key 的 HttpSessionState 对象,并对会话管理器进行同步工作。

状态管理器

  状态管理本来是一件很美好的事情,只可惜总是有些厂商在实现的时候考虑得不那么周全。例如在 ASP 中的状态管理实现就比较烂,因为只实现了一个进程内的基于内存的状态管理,故而存在很多问题:

  (1)所有的 Session 数据都保存在 Web 服务的进程中,会造成服务器支持会话数量受到服务器内存资源的限制问题,同时也因为大量非活动会话导致内存被无效占用。
  (2)服务器进程崩溃会导致所有的会话数据丢失。
  (3)会话无法跨进程或在负载均衡情况下使用,除非负载均衡技术保障同一用户每次都能被路由到同一机器上。就算这样也无法保障服务器崩溃造成的会话数据丢失。
  (4)需要 Cookie 的支持,而现在因为安全性问题,很多人在浏览器中关闭了 Cookie 和 js 的支持。

  为此 ASP 的使用者不得不自己手工将会话信息以会话 ID 为主键同步到外部数据库中,以缓解类似问题。

  而在 ASP.NET 中,因为设计时就考虑了这些问题,能够避免这些限制:

(1)支持进程外的状态管理,通过独立状态管理服务或 SQL Server 状态服务器管理会话状态。
(2)支持不使用 Cookie 的状态维护,通过在 URL 中自动增加会话 ID 来避免使用 Cookie。
(3)通过独立的状态管理服务或SQL Server 状态服务器支持负载均衡时同步使用会话信息。

0
相关文章