在ASP.NET中实现会话状态基础
|
简介 会话状态与构成 HTTP 协议和规范的任何逻辑实体都没有对应关系。会话是由服务器端开发环境(例如传统的 ASP 和 ASP.NET)构建的抽象层。ASP.NET 展示会话状态的方式以及会话状态的内部实现方式都取决于平台的基础结构。因此,传统的 ASP 和 ASP.NET 以完全不同的方式来实现会话状态,预计在下一版的 ASP.NET 中会有进一步的改进和增强。 在 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:状态客户端提供程序
[1] [2] 软件开发网 www.mscto.com
同步访问会话状态 } 此错误已经在 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 体系结构中。另外,会话状态存储在外部进程中,从根本上消除了由于进程循环而导致的周期性数据丢失的风险。 下面介绍如何使用 Windows NT 服务。正如上文所述,NT 服务是一个名为 aspnet_state.exe 的进程,通常位于 C:WINNTMicrosoft.NETFrameworkv1.1.4322 文件夹中。 实际目录取决于您实际运行的 Microsoft? .NET Framework 版本。使用状态服务器之前,应确保该服务就绪并正运行在用作会话存储设备的本地或远程计算机上。状态服务是 ASP.NET 的组成部分并与之一起安装,因此您无需运行其他安装程序。默认情况下,状态服务并没有运行,需要手动启动。ASP.NET 应用程序将在加载状态服务器之后立即尝试与之建立连接。因此,该服务必须准备就绪且正在运行,否则将引发 HTTP 异常。下图显示了该服务的属性对话框。 ![]() 图 2:ASP.NET 状态服务器的属性对话框 ASP.NET 应用程序需要指定会话状态服务所在的计算机的 TCP/IP 地址。必须将以下设置输入该应用程序的 web.config 文件中。 <configuration>; <system.web>; <sessionState mode="StateServer" stateConnectionString="tcpip=expoware:42424" />; </system.web>; </configuration>; stateConnectionString 特性包含计算机的 IP 地址以及用来进行数据交换的端口。默认的计算机地址为 127.0.0.1(本地主机),默认端口为 42424。您也可以按名称指示计算机。对于代码来说,使用本地或远程计算机是完全透明的。请注意,不能在该名称中使用非 ASCII 字符,并且端口号是强制的。 如果您使用进程外会话存储,会话状态将仍然存在并且可供将来使用,无论 ASP.NET 辅助进程出现何种情况。如果该服务被中断,数据将被保留下来,并且在该服务恢复时自动进行检索。但是,如果状态提供程序服务停止或失败,数据将丢失。如果您希望应用程序具有强大的功能,请使用 SQLServer 模式,而不要使用 StateServer 模式。 <configuration>; <system.web>; <sessionState mode="SQLServer" sqlConnectionString="server=127.0.0.1;uid=<user id>;;pwd=<password>;;" />; </system.web>; </configuration>; 您可以通过 sqlConnectionString 特性指定连接字符串。请注意,特性字符串必须包含用户 ID、密码和服务器名称。它不能包含 Database 和 Initial Catalog 之类的标记,因为此信息默认为固定名称。用户 ID 和密码可以替换为集成的安全设置。 如何创建数据库?ASP.NET 提供两对脚本来配置数据库环境。第一对脚本名为 InstallSqlState.sql 和 UninstallSqlState.sql,与会话状态 NT 服务位于同一个文件夹中。它们创建名为 ASPState 的数据库和几个存储的过程。但是,数据存储在 SQL Server 临时存储区域 TempDB 数据库中。这意味着,如果重新启动 SQL Server 计算机,会话数据将丢失。 要解决这一局限性,请使用第二对脚本。第二对脚本名为 InstallPersistSqlState.sql 和 UninstallPersistSqlState.sql。在这种情况下,将创建 ASPState 数据库,但是会在同一个数据库中创建数据表,而且这些数据表同样是持久的。为会话安装 SQL Server 支持时,还将创建一个作业,以删除会话状态数据库中过期的会话。该作业名为 ASPState_Job_DeleteExpiredSessions 并且一直运行。请注意,要使该作业正常进行,需要运行 SQLServerAgent 服务。 无论您选择哪种模式,为会话状态操作进行编码的方式都不会改变。您可以始终针对 Session 属性进行工作并像平常一样读取和写入值。所有行为上的差异都是在较低的抽象层上处理的。状态序列化或许是会话模式之间的最重要差异。
[1] [2]
状态序列化和反序列化 软件开发网 www.mscto.com 会话的生命周期 关于 ASP.NET 会话管理,重要的一点是,仅当将第一个项目添加到内存词典中时,会话状态对象的生命周期才开始。仅在执行如下代码片断后,才可以认为 ASP.NET 会话开始。 Session["MySlot"] = "Some data"; Session 词典通常包含 Object 类型,要向后读取数据,需要将返回的值转换为更具体的类型。 string data = (string) Session["MySlot"]; 当页面将数据保存到 Session 中时,会将值加载到 HttpSessionState 类包含的特制的词典类中。完成当前处理的请求时,会将词典的内容加载到状态提供程序中。如果由于未通过编程方式将数据放入词典而导致会话状态为空,则不会将数据序列化到存储介质中,而且更重要的是,不会在 ASP.NET Cache、SQL Server 或 NT 状态服务中创建槽来跟踪当前会话。这是出于性能方面的原因,但会对处理会话 ID 的方式产生重要影响:将为每个请求生成一个新的会话 ID,直到将某些数据存储到会话词典中。 需要将会话状态与正在处理的请求连接时,HTTP 模块会检索会话 ID(如果它不是启动请求),并在配置的状态提供程序中寻找它。如果没有返回数据,HTTP 模块将为请求生成一个新的会话 ID。这可以很容易地通过以下页面进行测试: <%@ Page Language="C#" Trace="true" %>; </html>; <body>; <form runat="server">; <asp:button runat="server" text="Click" />; </form>; </body>; </html>; 无论何时单击该按钮并返回页面,都将生成新的会话 ID,同时记录跟踪信息。 ![]() 图 3:在没有将数据存储到会话词典中的应用程序中,为每个请求生成一个新的会话 ID。 Session_OnStart 事件的情况如何呢?也会为每个请求引发该事件吗?如果应用程序定义 Session_OnStart 处理程序,则会始终保存会话状态,即使会话状态为空。因此,对于第一个请求之后的所有请求来说,会话 ID 始终为常量。仅在确实必要时,才使用 Session_OnStart 处理程序。 如果会话超时或被放弃,下次访问无状态应用程序时,其会话 ID 不会发生改变。经过设计后,即使会话状态过期,会话 ID 也能持续到浏览器会话结束。也就是说,只要浏览器实例相同,就始终使用同一个会话 ID 表示多个会话。 Session_OnEnd 事件标志着会话的结束,并用于执行终止该会话所需的所有清除代码。但请注意,只有 InProc 模式支持该事件,也就是说,只有将会话数据存储在 ASP.NET 辅助进程中时才支持该事件。对于要引发的 Session_OnEnd 事件来说,必须首先存在会话状态,这意味着必须在该会话状态中存储一些数据,并且必须至少完成一个请求。 在 InProc 模式下,作为项目添加到缓存中的会话状态被赋予一个可变过期时间策略。可变过期时间表示如果某个项目在一定时间内没有使用,将被删除。在此期间处理的任何请求的过期时间都将被重置。会话状态项目的时间间隔被设置为会话超时。用来重置会话状态过期时间的技术非常简单和直观:会话 HTTP 模块只读取 ASP.NET Cache 中存储的会话状态项目。如果知道 ASP.NET Cache 对象的内部结构,该模块将进行计算以重新设置可变过期时间。因此,当缓存项目过期时,会话已超时。 过期的项目将自动从缓存中删除。状态会话模块作为此项目的过期时间策略的一部分,也代表了一个删除回调函数。缓存将自动调用删除函数,删除函数然后将引发 Session_OnEnd 事件。如果应用程序通过进程外组件来执行会话管理,则永远不会引发结束事件。
[1] [2]
Cookieless 会话 HttpCookie sessionCookie; sessionCookie = new HttpCookie("ASP.NET_SessionId", sessionID); sessionCookie.Path = "/"; 会话 Cookie 的过期时间很短,在每个请求成功后更新过期时间。Cookie 的 Expires 属性表示 Cookie 在客户端的过期时间。如果未显式设置会话 Cookie,Expires 属性将默认为 DateTime.MinValue,即 .NET Framework 允许的最小时间单位。 要禁用会话 Cookie,请在配置文件中将 Cookieless 特性设置为 true,如下所示: <configuration>; <system.web>; <sessionState Cookieless="true" />; </system.web>; </configuration>; 此时,假设您请求以下 URL 处的页面: http://www.contoso.com/sample.aspx 浏览器地址栏中实际显示的内容会有所不同,现在包含会话 ID,如下所示: http://www.contoso.com/(5ylg0455mrvws1uz5mmaau45)/sample.aspx 实例化会话状态 HTTP 模块时,该模块将检查 Cookieless 特性的值。如果为 true,则将请求重定向 (HTTP 302) 到经过修改的包含会话 ID(紧跟在页面名称前)的虚拟 URL。再次处理请求时,请求中会包含该会话 ID。如果请求启动新的会话,HTTP 模块将生成新的会话 ID,然后重定向该请求。如果回传请求,则会话 ID 已经存在,因为回传使用相对 URL。 软件开发网 www.mscto.com 使用 Cookieless 会话的缺点是,如果调用绝对 URL,将丢失会话状态。使用 Cookie 时,您可以清除地址栏,转至其他应用程序,然后返回上一个应用程序并检索相同的会话值。如果在禁用会话 Cookie 时执行此操作,将丢失会话数据。例如,以下代码将打断该会话: <a runat="server" href="/code/page.aspx">;Click</a>; 如果需要使用绝对 URL,请通过一些小技巧手动将会话 ID 添加到 URL 中。您可以对 HttpResponse 类调用 ApplyAppPathModifier 方法。 <a runat="server" href=<% =Response.ApplyAppPathModifier("/code/page.aspx")%>; >;Click</a>; ApplyAppPathModifier 方法将使用表示 URL 的字符串,并返回嵌入会话信息的绝对 URL。例如,需要从 HTTP 页面重定向到 HTTPS 页面时,此技巧特别有用。 小结 会话状态最初由传统的 ASP 引入,它是基于词典的 API,使开发人员能够存储会话期间的自定义数据。在 ASP.NET 中,会话状态支持以下两种主要功能:Cookieless 会话 ID 存储和传输,以及会话数据实际存储的状态提供程序。为实现这两种新功能,ASP.NET 利用 HTTP 模块控制会话状态与正在处理的请求上下文之间的绑定。 在传统的 ASP 中,使用会话状态就是指使用 Cookie。在 ASP.NET 中已不再如此,因为可以使用 Cookieless 架构。借助 HTTP 模块的力量,可以分解请求的 URL 以使其包含会话 ID,然后将其重定向。接下来,HTTP 模块会从该 URL 中提取会话 ID 并使用它检索任何存储的状态。 会话的物理状态可以存储在三个位置:进程内内存、进程外内存和 SQL Server 表。数据必须经过序列化/反序列化处理,才能供应用程序使用。HTTP 模块会在请求开始时将会话值从提供程序复制到应用程序的内存中。请求完成后,修改后的状态将返回提供程序。这种数据通信会对性能产生不同程度的不利影响,但是会大大增强可靠性和稳定性,也使对 Web Farm 和 Web Garden 体系结构的支持更容易实现。 [1] [2]
源代码网推荐 源代码网供稿. |



