您的位置:首页 > 编程语言 > ASP

把会话数据持久存储到远程服务器中

2007-06-27 21:55 204 查看
 

13.3.3 把会话数据持久存储到远程服务器中

通过利用以下两种预定义的进程外状态提供程序之一:StateServer和SQLServer,可以巧妙地解决前文所述的InProc模式下的会话状态丢失问题。然而在这种情况下,会话状态存储在ASP.NET工作进程之外,并需要一个额外的代码层把它序列化到实际的存储介质,以及从中反序列化出会话状态。

需要把会话状态从一个外部存储库复制到本地会话字典中,可能使状态管理进程的性能下降15%到25%。然而要注意的是,这仅仅是一个粗略估计,更接近于最小影响,而不是最大影响。实际上,该估计并没有充分考虑实际存储到会话状态中的类型的复杂度。

警告 选择一个进程外的状态提供程序(例如,StateServer和SQLServer)时,要注意一点:在应用程序投入生产之前需要设置运行时环境。这就是说,要么为StateServer启动一个Windows服务,要么为SQLServer配置一个数据库。如果保持默认的、进程内选项,则不需要任何预备工作。

1. 状态序列化和反序列化

使用InProc模式时,对象作为类的活实例存储在会话状态中。根本不会发生真正的序列化和反序列化,这就是说,实际上可以把我们创建的任何对象(包括COM对象)存储在Session中,而且访问它们不会带来很大的开销。如果选择一个进程外的状态提供程序,情况就没有这么好了。

在一个进程外的体系结构中,会话值从本机存储介质复制到处理该请求的AppDomain的内存中。为了完成此任务,需要一个序列化/反序列化层,这也代表了进程外状态提供程序的主要代价。这对我们的代码有何影响呢?首先,应当确保只把可序列化的对象存储在会话字典中;否则,不能保存会话状态,这一点是不难想象的。

为了执行类型的序列化和反序列化,ASP.NET使用两个方法,其中每个方法提供不同的性能结果。对于基本类型,ASP.NET利用一个优化的内部序列化程序;对于其他类型,包括对象和用户定义的类,ASP.NET使用更慢的.NET二进制格式化程序。基本类型为string, DateTime,Guid,IntPtr,TimeSpan,Boolean,byte,char,以及所有的数值类型。

优化的序列化程序(一个名为AltSerialization的内部类)利用BinaryWriter对象的一个实例写出一个表示类型的字节,然后写出值。读取时,AltSerialization类首先提取一个字节,检测要读取的数据的类型,然后利用BinaryReader类的一个特定类型的方法获取数据。该类型根据一个内部表与一个索引关联,如图13.4所示。



图13.4 内部类AltSerialization使用的基本类型序列化方案

注意

 

虽然布尔类型和数值类型有着众所周知的长度,但字符串的长度却变化很大。阅读器如何确定一个字符串的正确长度呢?BinaryReader.ReadString方法利用这样的事实,即在底层流上,字符串总是以长度为前缀,并且作为整型数每次7位进行编码。另一方面,DateTime类型的值通过只写入形成日期的总滴答数进行存储,并作为一个Int64类型读取。
 

如前所述,只要相关类型标记为可序列化,更复杂的对象用较慢的BinaryFormatter类进行序列化。简单类型和复杂类型都使用相同的流,但是所有的非基本类型都用相同的类型ID标识。性能下降15%到25%是根据使用基本类型的假设粗略估计的。使用的类型越复杂,开销增长得越快,并且可靠的数字只能通过测试特定的应用场景计算出来。

根据这一特性,如果打算使用进程外的会话,则一定要有效地存储数据。例如,如果需要持久地存储一个有3个字符串属性的类的实例,也许最好使用3个用基本类型填充的不同的槽,而不是一个需要二进制格式化程序的会话槽。然而要明白的是,这只是一个因情况而异的指导原则,值得怀疑。

警告 在经典ASP中,把一个ADO Recordset对象存储在会话状态中,由于读取问题,是一个危险的动作。幸运的是,在ASP.NET中不存在使我们烦恼的线程相关问题。然而,并非只要把任何对象都存储在Session中就万事大吉啦。如果使用进程外的方案,那么在存储DataSet对象时,应当非常小心。具体原因与DataSet类的序列化过程有关。因为DataSet是一个复杂类型,所以通过二进制格式化程序进行序列化。然而,DataSet的序列化过程生成许多XML数据,最后成为会导致一个严重的问题,特别是存储大量数据的大型应用程序。实际上,很容易发现自己为每个请求移动了几兆数据。只需在ASP.NET 1.x进程外会话中避免DataSet对象,并且选择列和行数据的普通数组。在ASP.NET 2.0中,先设置新的RemotingFormat属性,然后再存储它。

2. 存储会话数据

在StateServer模式下工作时,HttpSessionState对象的全部内容被序列化到一个外部应用程序——即,一个名为aspnet_state.exe的Microsoft Windows NT服务。请求完成后调用该服务来序列化会话状态。该服务在内部用一个字节数组存储每个会话状态。当一个新的请求开始处理时,与给定的会话ID相对应的数组复制到一个内存流中,然后反序列化到一个内部的会话状态项对象。该对象实际表示整个会话的内容。页面实际使用的HttpSessionState对象只是它的应用程序接口。

如前所述,非基本类型使用系统的二进制格式程序(binary formatter)类,它只能处理显式地标记为可序列化的类。这就是说,COM对象(要么以编程的方式创建,要么在global.asax中声明为具有一个会话范围的静态对象)不能被一个进程外的状态提供程序使用。任何不可序列化的对象都存在这样的局限性。

3. 配置StateServer提供程序

使用进程外的存储场景,为会话状态提供了一个更长的生命,从而使应用程序更健壮。进程外的会话状态存储主要保护会话不受IIS和ASP.NET进程故障的影响。通过将会话状态与页面本身相分离,更容易将一个现有的应用程序扩展到Web farm和Web garden体系结构。此外,存储在一个外部进程中的会话状态,从根本上消除由于进程回收而定期丢失数据的风险。

如前所述,ASP.NET会话提供程序是一个名为aspnet_state.exe的Windows NT服务。该服务一般驻留在ASP.NET的安装文件夹中:

 

%WINDOWS%/Microsoft.NET/Framework/[version]

 

通常要注意的是,最终的目录取决于我们实际运行的.NET Framework版本。在使用状态服务器之前,应当确保该服务正在作为会话存储的本地或远程机器上运行。状态服务是ASP.NET的一个组成部分,并与它一起被安装,因而不需要运行额外的安装步骤。

在默认情况下,该状态服务被停止,需要人工启动它。通过该服务的属性对话框,可以改变它的配置,如图13.5所示。



图13.5 ASP.NET状态服务器的属性对话框
ASP.NET应用程序需要指定托管该会话状态服务的机器的TCP/IP地址。如下清单展示了为启用远程会话状态而需要对web.config文件进行的修改:

 

<configuration>

<system.web>

<sessionState

mode="StateServer"

stateConnectionString="tcpip=MyMachine:42424" />

</system.web>

</configuration>

 

要注意的是,赋给mode属性的值是区分大小写的。stateConnectionString属性的格式如下面一行代码所示。默认的机器地址是127.0.0.1,而端口是42424。

 

stateConnectionString="tcpip=server:port"

 

服务器名称既可以是IP地址,也可以是机器名。然而在这种情况下,名称不支持非ASCII字符。最后,端口号是强制要求的,不能省略。

 

重要提示

状态服务器不反对请求者的任何身份验证屏障,意味着可以访问该网络的任何人都可以自由地访问会话数据。为了保护会话状态并确保只有Web服务器机器能够访问它,可以使用防火墙或IPSec策略。另一种与安全相关的措施涉及改变默认的端口号。要改变端口号,编辑注册键之下的Port项:HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/aspnet_state/Parameters。在web.config文件中编写端口是不够的。
 

ASP.NET应用程序试图在加载之后立即连接到会话状态服务器。aspnet_state服务必须正在运行;否则会抛出一个HTTP异常。在默认情况下,这个服务没有被配置成自动启动。它使用.NET Remoting来回移动数据。

注意

 

ASP.NET状态提供程序运行在ASP.NET帐户下。然而,使用服务控制管理器(Service Control Manager)接口可以任意地配置和改变该帐户。该状态服务小而简单,没有实现任何特别的特征。它只能存储数据和在指定端口侦听要服务的请求。具体说来,该服务并不是clusteraware(即它没有提供一个故障保护监控器(failover monitor)来实现容错功能),并在另一个服务器接管一个发生故障的服务器时,该服务不能在群集环境中使用。最后要注意的是,在默认情况下,状态服务器只侦听本地连接。如果状态服务器和Web服务器位于不同的机器上,则需要启用远程连接。对此使用前面所列的注册键中的另一个注册项。该项是AllowRemoteConnection,并且必须设置为非0值。
 

13.3.4 把会话数据持久存储在SLQ Server中

在一个外部进程中维护会话状态无疑会使整个ASP.NET应用程序更稳定。不管工作进程发生了什么,会话状态仍然在那里,准备进一步使用。如果服务被暂停,则把会话数据保存起来,并在服务恢复时自动获取这些数据。不幸的是,如果状态服务被停止或者发生一个故障,则数据被丢失。如果健壮性是应用程序的关键,则取消StateServer模式而采用SQLServer模式。

1. 性能和健壮性

当ASP.NET工作于SQLServer模式时,会话数据存储在一个量身定制的数据库表。结果是,即使SQL Server崩溃了,会话数据也能幸免于难,但必须增加更大的开销。SQLServer模式允许我们把数据存储在任何连接的机器上,前提是该机器SQL Server 7.0或更新的版本。除了不同的介质之外,存储机制和远程服务器的差不多。具体说来,序列化和反序列化算法是相同的,只是由于存储器的特征它的速度慢一点。存储基本类型的数据时,建立页面的Session对象所需的时间一般至少要比InProc模式下长25%。还有一点,使用的数据类型越复杂,管理会话数据所需的时间越长。

注意

 

如果必须决定使用状态服务器还是SQL服务器存储,则考虑SQL Server是支持集群的事实,它使基于它的解决方案比基于状态服务器的更健壮、更可靠。
 

2. 配置支持SQL Server的会话状态

将SQL Server用作状态提供程序,在web.config文件的<sessionState>节输入如下变更:

 

<configuration>

<system.web>

<sessionState

mode="SQLServer"

sqlConnectionString="server=127.0.0.1;integrated security=SSPI;" />

</system.web>

</configuration>

 

具体说来,需要把mode属性(区分大小写)设置为SQLServer,并通过sqlConnectionString属性指定连接字符串。注意,sqlConnectionString属性字符串必须包括一个用户ID、密码和服务器名称。然而,它不能包含Database和Initial Catalog等标记,除非(而且只有在ASP.NET 2.0下)启用一个定制数据库,如表13.8所示。只有在启用allowCustomSqlDatabase配置设置时,才可以指定一个SQL Server Initial Catalog服务器名称,或者在连接字符串中使用SQL Server Express attachDBFileName指向一个MDB文件。如果该设置被禁用,则在连接字符串中指定这些设置的任何企图都会抛出一个异常。

注意

 

在ASP.NET 2.0中,一个进程外的会话状态实现(包括SQLServer和StateServer)的连接字符串,也可以参照<connectionStrings>节中定义的连接字符串进行指定。会话状态模块首先用合适的<sessionState>属性中指定的名称在<connectionString>节中查找一个连接字符串。如果没有找到,则会话状态将直接使用这个指定的字符串。
 

至于访问数据库的凭据,既可以使用用户ID和密码,也可以使用集成安全性。

注意

 

在SQL Server中,不管使用什么帐户来访问会话状态,至少必须授予它db_datareader和db_datawriter权限。还要注意的是,要对SQL Server环境进行配置以存储会话状态,则需要管理特权,因为需要创建一个新的数据库和存储过程。
 

在ASP.NET 2.0中,SQL Server模式下的会话状态,现在支持定制的命令超时值(以秒为单位)规范,以适应响应缓慢的服务器场景。可通过sqlCommandTimeout属性控制这个值,如表13.8所示。

3. 创建SQL Server数据存储区

ASP.NET提供了两对配置数据库环境的脚本,用于创建任何所需的表、存储过程、触发器和作业。第1对脚本称为InstallSqlState.sql和UninstallSqlState.sql。它们创建一个称为ASPState的数据库和几个存储过程。然而,数据存储在两个属于TempDB数据库的表中。在SQL Server中,TempDB为临时表、临时的存储过程和其他临时的工作存储需求提供了存储区。这表明,如果SQL Server机器重新启动,则会话数据被丢失。

第2对脚本称为InstallPersistSqlState.sql和UninstallPersistSqlState.sql。在这种情况下,也是创建一个ASPState数据库,但是其中的表是持久性的,因为它们在相同的数据库内创建。所有的脚本都位于如下路径中:

 

%SystemRoot%/Microsoft.NET/Framework/[version]

 

重要提示
 

在ASP.NET 2.0中,之所以包含这些脚本文件只是为向后兼容。我们应当使用aspnet_regsql.exe安装和卸载一个SQL会话状态。具体说来,最新的aspnet_regsql.exe支持多个选项,比如使用定制的数据库表等。在ASP.NET 1.x中,也有一个aspnet_regsql版本,但是存在一些局限性——既没有持久性表,也没有定制表。
 

新建的表称为ASPStateTempApplications和ASPStateTempSessions。图13.6展示了SQL Server中会话数据库的一个视图。



图13.6 SQL Server 2000中的ASPState数据库
ASPStateTempApplications表为当前正在运行的每个ASP.NET应用程序定义一个记录。表13.9列出了该表包含的列。

表13.9 ASPStateTempApplications表


类 型
描 述
AppId

Int

索引字段。它表示一种自动生成的ID,标识一个在SQLServer会话模式运行的应用程序

AppName

char(280)

指出运行该应用程序的AppDomain的应用程序ID。它匹配HttpRuntime对象上的AppDomainAppId属性的内容

 

ASPStateTempSessions表存储实际的会话数据。该表为每个活动会话包含一行。表13.10概括了该表的结构。

SessionItemLong列包含数据的一个长二进制块。虽然用户总是像一个长长的字节序列那样使用图像数据,但是数据并不是以这种格式存储的。数据存储在一个8 KB页面的集合中,而且这些页面不一定相邻。

安装会话的SQL Server支持时,还要创建一个从会话状态数据库中删除过期会话的作业。如图13.7所示,该作业称为ASPState_Job_DeleteExpiredSessions,默认的配置使它每分钟都在运行。应当注意的是,SQLServerAgent服务需要正在运行。

表13.10 ASPStateTempSessions表


类 型
描 述
SessionId

char(88)

索引字段,它表示会话ID

Created

DateTime

指出会话被创建的时间。默认值为当前时间

Expires

DateTime

指出会话将到期的时间。该值一般等于会话状态的创建时间加上Timeout中指定的分钟数。注意,Created指会话的创建时间,而Expires把分钟数加到第一个数据项被添加到会话状态的时间

LockDate

DateTime

指出会话被锁定以添加最后一个数据项的时间。该值表示为当前的UTC(Universal Time Coordinate)时间

LockDateLocal

DateTime

与LockDate一样,但是它只表示系统的本地时间。ASP.NET 1.x不支持该列

LockCookie

int

指出该会话被锁定的次数——即,访问次数

Timeout

int

指出会话的超时时间(以分为单位)

Locked

bit

指出会话当前没有被锁定

SessionItemShort

VarBinary(7000)

可以取null的字段。它表示指定会话中的值。这些字节的布局等同于StateServer提供程序所述的布局。如果对字典进行序列化需要7000多字节,则使用SessionItemLong

SessionItemLong

Image

可以取null的字段,表示一个超过7000字节的会话状态的序列化版本

Flags

Int

指示SessionStateActions枚举类型的行动标记(初始化数据项)。ASP.NET 1.x不支持该列

 



图13.7 删除过期会话的SQL Server作业

4. 恢复宿主标识

在ASP.NET 1.x中,用来访问SQL Server存储的会话状态的凭据取决于连接字符串。如果显式地提供安全性,则使用用户名和密码访问数据库。否则,如果要求集成安全性,则使用当前已登录客户的帐户。这种方法与StateServer状态提供程序不匹配,后者使用ASP.NET标识完成此项任务。然而,更重要的是,它对使用假冒客户(client impersonation)的内联网站点提出了一些问题。实际上,在这些情况下,必须把数据库访问权限授予可能发出调用的每个客户帐户。

在ASP.NET 2.0中,useHostingIdentity属性(如表13.8所示)允许我们决定实际使用的标识。在ASP.NET 2.0中,当SQLServer状态提供程序与集成安全性结合使用时,突破了ASP.NET行为,该标识是被ASP.NET假冒的标识。它通常是ASPNET或NETWORK SERVICE或者被ASP.NET工作进程通过配置文件的<identity>节假冒的任何其他帐户。这样就简化了内联网站点的管理体验,只要求ASP.NET帐户被授予队保护资源和关键资源的访问。useHostingIdentity属性值默认为true,使我们能够在调用SQLServer会话状态提供程序之前恢复到ASP.NET标识。如果使用一个定制提供程序,也会发生这种情况。

注意

 

如果使用Windows集成验证访问SQL Server,则从安全角度考虑,高度建议恢复到宿主身份。否则,建议创建一个特定的帐户,只授予它执行会话状态存储过程和访问相关资源的权力。
 

Web farm场景中的会话状态

为了在Web farm或Web garden硬件配置下运行而设计的ASP.NET应用程序,不能实施进程内的会话状态。InProc模式不适用于Web farm,因为一个不同的工作进程将在每个连接的机器上运行,并且每个进程维护自己的会话状态。该模式也不适用于Web garden,因为多个工作进程可以在相同的机器上运行。

将所有状态与工作进程分开,允许我们把一个应用程序分割到多个工作进程上,即使这些工作进程在多台计算机上也可以这样分割。在Web farm和Web garden场景中,只能有一个StateServer或SQLServer进程来提供会话状态管理。

如果正在运行一个Web farm,则一定要使所有的Web服务器中有相同的<machineKey>。(有关的更多内容,参见Knowledge Base文章Q313091。)此外,为了把会话状态保存在Web farm中的不同服务器上,则所有的应用程序在IIS元数据库中应当有相同的应用程序路径。该值作为AppDomain应用程序ID进行设置,并标识ASP.NET状态数据库中一个正在运行的应用程序。(更多内容,请参见Knowledge Base文章Q325056。)

在ASP.NET 2.0中,还引入了分区解析器(partition resolver),以允许会话状态提供程序把它的数据划分到多个后端节点上。这就允许我们根据定制的、用户定义的负载平衡机制,把会话状态扩展到大型Web farm上。分区提供程序是一个组件,将连接字符串(实际字符串,而不是指向web.config文件中的一个字符串的指针)提供给会话状态,用来访问数据,重写<sessionState>节中的任何其他设置。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐