asp.net中如何实现会话状态

分类: asp.net   出处:iocblog整理  更新时间:2009-03-14   添加到收藏  

 简介

  在 web 应用程序这样的无状态环境中,了解会话状态的概念并没有实际的意义。尽管如此,有效的状态管理对于大多数 web 应用程序来说都是一个必备的功能。microsoft asp.net 以及许多其他服务器端编程环境都提供了一个抽象层,允许应用程序基于每个用户和每个应用程序存储持久性数据。

  需要特别注意的是,web 应用程序的会话状态是应用程序在不同的请求中缓存和检索的数据。会话表示用户在与该站点连接期间发送的所有请求,会话状态是用户在会话期间生成和使用的持久性数据的集合。每个会话的状态都彼此独立,而且在用户会话结束时就不复存在了。

  会话状态与构成 http 协议和规范的任何逻辑实体都没有对应关系。会话是由服务器端开发环境(例如传统的 asp 和 asp.net)构建的抽象层。asp.net 展示会话状态的方式以及会话状态的内部实现方式都取决于平台的基础结构。因此,传统的 asp 和 asp.net 以完全不同的方式来实现会话状态,预计在下一版的 asp.net 中会有进一步的改进和增强。

  本文讨论如何在 asp.net 1.1 中实现会话状态,以及如何在被管理的 web 应用程序中优化会话状态管理。

  asp.net 会话状态概述

  会话状态并不是 http 基础结构的一部分。也就是说,应该有一个结构组件将会话状态与每个传入请求绑定在一起。运行时环境(传统的 asp 或 asp.net)能够接受 session 之类的关键字,并使用它指示服务器上存储的数据块。要成功解析 session 对象的调用,运行时环境必须将会话状态添加到正在处理的请求的调用上下文中。完成此操作的方式因平台而异,但它是有状态 web 应用程序的基础操作。

  在传统的 asp 中,会话状态是作为 asp.dll 库中包含自由线程 com 对象来实现的。(您对此很好奇吗?其实该对象的 clsid 是 d97a6da0-a865-11cf-83af-00a0c90c2bd8。)此对象存储以名称/值对集合的方式组织的数据。“名称”占位符表示用来检索信息的关键字,而“值”占位符表示会话状态中存储的内容。名称/值对按照会话 id 进行分组,这样,每个用户看到的只是他/她自己创建的名称/值对。

  在 asp.net 中,会话状态的编程接口与传统的 asp 几乎是相同的。但它们的基础实现是完全不同的,前者比后者更具有灵活性、可扩展性和更强的编程功能。深入研究 asp.net 会话状态之前,让我们简单回顾一下 asp.net 会话基础结构的某些结构功能。

  在 asp.net 中,任何传入 http 请求都要通过 http 模块管道进行传输。每个模块都可以筛选并修改请求所携带的大量信息。与每个请求关联的信息叫做“调用上下文”,编程中用 httpcontext 对象来表示。我们不应将请求的上下文视为状态信息的另一个容器,虽然它提供的 items 集合只是一个数据容器。httpcontext 对象不同于所有其他状态对象(例如,session、application 和 cache),因为它的有限生命周期超出了处理请求所需的时间。当请求通过一系列注册的 http 模块后,其 httpcontext 对象将包含状态对象的引用。当最终可以处理请求时,关联的调用上下文将绑定到特定会话 (session) 和全局状态对象(application 和 cache)。

  负责设置每个用户的会话状态的 http 模块为 sessionstatemodule。该模块的结构是根据 ihttpmodule 接口设计的,它为 asp.net 应用程序提供大量与会话状态有关的服务。包括生成会话 id、cookieless 会话管理、从外部状态提供程序中检索会话数据以及将数据绑定到请求的调用上下文。

  http 模块并不在内部存储会话数据。会话状态始终保存在名为“状态提供程序”的外部组件中。状态提供程序完全封装会话状态数据,并通过 istateclientmanager 接口的方法与其他部分进行通信。会话状态 http 模块调用该接口上的方法来读取并保存会话状态。asp.net 1.1 支持三种不同的状态提供程序,如表 1 所示。

表 1:状态客户端提供程序

提供程序 说明 
inproc 会话值在 asp.net 辅助进程(microsoft? windows server? 2003 中的 aspnet_wp.exe 或 w3wp.exe)的内存中保持为活动对象。这是默认选项。 
stateserver 会话值被序列化并存储在单独进程 (aspnet_state.exe) 的内存中。该进程还可以在其他计算机上运行。
sqlserver 会话值被序列化并存储在 microsoft? sql server? 表中。sql server 的实例可以在本地运行,也可以远程运行。


  会话状态 http 模块将从 web.config 文件的 <sessionstate>; 部分读取当前选定的状态提供程序。

<sessionstate mode="inproc | stateserver | sqlserver />;

  根据 mode 特性的值,将通过不同的步骤从不同的进程中检索会话状态并将其存储到不同的进程中。默认情况下,会话状态存储在本地的 asp.net 辅助进程中。特殊情况下,会将其存储在 asp.net cache 对象的专用槽中(不能通过编程方式访问)。也可以将会话状态存储在外部,甚至是远程进程中(例如,名为 aspnet_state.exe 的 windows nt 服务中)。第三个选项是将会话状态存储到由 sql server 2000 管理的专用数据库表中。

  http 模块会在请求的一开始对会话值进行反序列化,使它们成为词典对象。然后,将采用编程方式通过类(例如,httpcontext 和 page)显示的属性 session 来访问词典(实际上是 httpsessionstate 类型的对象)。会话状态值与开发人员可见的会话对象之间的绑定将持续到请求结束。如果请求成功完成,所有状态值将被序列化回状态提供程序,并可用于其他请求。

  图 1 说明了请求的 asp.net 页面与会话值之间的通信。每个页面所使用的代码都与 page 类上的 session 属性有联系。其编程方式与传统的 asp 几乎相同。


图 1:asp.net 1.1 中的会话状态体系结构

  在完成请求所需的时间内,会话状态的物理值处于锁定状态。该锁定由 http 模块在内部管理并用于同步对会话状态的访问。

  会话状态模块实例化应用程序的状态提供程序,并使用从 web.config 文件中读取的信息对其进行初始化。接下来,每个提供程序将继续自己的初始化操作。提供程序的类型不同,其初始化操作会大不相同。例如,sql server 状态管理器将打开与给定数据库的连接,而进程外管理器将检查指定的 tcp 端口。另一方面,inproc 状态管理器将存储对回调函数的引用。从缓存中删除元素时将执行此操作,并用于触发应用程序的 session_onend 事件。

  同步访问会话状态

  当 web 页对 session 属性进行非常简单且直观的调用时,究竟会出现什么情况呢?许多操作都是在后台进行的,如下面的繁琐代码所示:

int sitecount = convert.toint32(session["counter"]);

  上述代码实际上访问的是 http 模块创建的会话值在本地内存中的副本,从特定状态提供程序(参见图 1)中读取数据。如果其他页面也试图同步访问该会话状态,又会如何呢?这种情况下,当前的请求可能会停止处理不一致的数据或过时的数据。为了避免这种情况,会话状态模块将实现一个读取器/写入器锁定机制,并对状态值的访问进行排队。对会话状态具有写入权限的页面将保留该会话的写入器锁定,直到请求终止。

  通过将 @page 指令的 enablesessionstate 属性设置为 true,页面可以请求会话状态的写入权限。(这是默认设置)。但是,页面还可以拥有会话状态的只读权限,例如,当 enablesessionstate 属性被设置为 readonly 时。在这种情况下,模块将保留该会话的读取器锁定,直到该页面的请求结束。结果将发生并发读取。

  如果页面请求设置一个读取器锁定,同一会话中同时处理的其他请求将无法更新会话状态,但是至少可以进行读取。也就是说,如果当前正在处理会话的只读请求,那么等候的只读请求要比需要完全访问权限的请求具有更高的优先权。如果页面请求为会话状态设置一个写入器锁定,那么所有其他页面都将被阻止,无论它们是否要读取或写入内容。例如,如果同时有两个框架试图在 session 中写入内容,一个框架必须等到另一个框架完成后才能写入。

  比较状态提供程序

  默认情况下,asp.net 应用程序将会话状态存储在辅助进程的内存中,特别是 cache 对象的专用槽中。选中 inproc 模式时,会话状态将存储在 cache 对象内的槽中。此槽被标记为专用槽,无法通过编程方式进行访问。换句话说,如果枚举 asp.net 数据缓存中的所有项目,将不会返回类似于给定会话状态的任何对象。cache 对象提供两类槽:专用槽和公用槽。编程人员可以添加和处理公用槽,但专用槽只能由系统(特别是 system.web 部件中定义的类)专用。

  每个活动会话的状态都占用缓存中的一个专用槽。槽的名称根据会话 id 进行命名,其值是名为 sessionstateitem 的内部未声明类的一个实例。inproc 状态提供程序获取会话 id 并在缓存中检索对应的元素。然后将 sessionstateitem 对象的内容输入 httpsessionstate 词典对象,并由应用程序通过 session 属性进行访问。请注意,asp.net 1.0 中存在一个错误,使 cache 对象的专用槽可以通过编程方式进行枚举。如果您在 asp.net 1.0 下运行以下代码,则能够枚举与每个当前活动会话状态中包含的对象对应的项目。

foreach(dictionaryentry elem in cache)
{
response.write(elem.key + ": " + elem.value.tostring());
}

  此错误已经在 asp.net 1.1 中得到解决,当您枚举缓存的内容时,将不再列出任何系统槽。

  到目前为止,inproc 可能是最快的访问选项。但请记住,会话中存储的数据越多,web 服务器所消耗的内存就越多,这样会潜在地增加性能降低的风险。如果您计划使用任何进程外解决方案,应该认真考虑一下序列化和反序列化可能带来的影响。进程外解决方案使用 windows nt 服务 (aspnet_state.exe) 或 sql server 表来存储会话值。因此,会话状态保留在 asp.net 辅助进程之外,并且需要使用额外的代码层,在会话状态和实际的存储介质之间进行序列化和反序列化操作。只要处理请求就会发生此操作,而且随后必须对其进行最高程度的优化。

  因为需要将会话数据从外部储备库复制到本地会话词典中,所以请求导致性能下降了 15%(进程外)到 25% (sql server)。请注意,虽然这只是一种粗略的估计,但它应该接近于最低程度的影响,最高程度的影响将远高于此。实际上,这种估计并没有完全考虑到会话状态中实际保存的类型的复杂程度。

  在进程外存储方案中,会话状态存活的时间较长,使应用程序的功能更强大,因为它可以防止 microsoft? internet 信息服务 (iis) 和 asp.net 失败。通过将会话状态与应用程序相分离,您还可以更容易地将现有应用程序扩展到 web farm 和 web garden 体系结构中。另外,会话状态存储在外部进程中,从根本上消除了由于进程循环而导致的周期性数据丢失的风险。

[1] [2] [3] 下一页



文章整理:iocblog
版权申明:本站文章均来自网络,如有侵权,请联系我们,我们收到后立即删除,谢谢!
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有。