使用Session State快速、可扩展、安全的管理Web程序
由于http协议的无状态性,Web应用程序一般都肩负着管理用户状态的责任。幸运的是,ASP.NET提供了很多方法去保持用户的状态,在这些方法中最有力的方法是会话状态。这个功能提供一个合适的、程序的界面用于将一个用户会话连接任意程序状态,也可以照看存储状态的后端和为程序管理客户端会话。这篇文章深入研究了设计和配置高性能、稳定、安全的会话解决方案,并且介绍直接来自ASP.NET功能组中的关于已经存在和新的ASP.NET会话状态的很好的练习。
ASP.NET会话状态让你可以将一个服务器端字符串或对象词典与一个特定的HTTP客户端会话相关联。一个会话被定义成一系列由同一个客户端在特定时间段发出的请求,并且通过关连会话的ID同每个唯一的客户端的方法进行管理。客户端的每一次请求都会产生一个ID,这些ID存放在Cookie中或者作为请求的URL的特殊部分来存储。会话数据被存储在服务器端的一个会话状态仓库中,会话状态仓库包括三种,进程内内存、SQL Server数据库和ASP.NET状态服务服务器。后两种模式使得会话状态通过网络可以共享地被多个网络服务器使用,并且并不需要和服务器有密切关系(就是说,会话不依赖于一个特定的网络服务器)。
会话状态运行时间工作由SessionStateModule类来实现,这个类将请求处理渠道作为一个IHttpModule插入到程序中。SessionStateModule在AcquireRequestState传递场景中的处理程序执行完成之前执行,在ReleaseRequestState传递场景中的处理程序执行完成之后执行(查看图例1)。在AcquireRequestState场景中,SessionStateModule尝试从请求中抽取出会话ID,从会话状态存储提供程序重新得到这个会话ID的会话数据。如果会话ID是当前的并且状态被成功地重新得到,模块生成被处理程序用于检查和改变会话状态的会话状态词典。
在ASP.NET 2.0中,会话抽取被压缩在会话ID管理器模块中,为了支持自定义会话ID管理模式,管理器模块可以被自定义实现工具替换(例如,将会话ID存储在查询字符串中或者放在表单字段中)。默认的会话ID管理器支持基于Cookie和基于URL的会话ID。ASP.NET 2.0会话ID管理器也默认提供支持,根据客户端的设备形式或者运行时间商议来自动检测对于每一个请求应该使用哪个会话ID模式。
如果在请求中部存在会话ID,申请者对会话进行了一些修改,那么一个新的会话ID将在ReleaseRequestState中产生,并且这个新的ID将会被会话ID管理器供给工具发布到客户端。在这种情况下,如果对已有会话进行修改,则SessionStateModule将使用状态存储提供器来保持对于相应会话的会话数据的修改。
在ASP.NET 2.0中,会话状态存储功能压缩在会话存储提供者模块里,这个模块可以内置在进程内、SQL Server数据库中或者状态服务器提供程序中,也可以是用来执行获取会话数据、会话创造物,保存对已有会话修改的自定义提供程序。
改善性能
在ASP.NET应用程序中使用会话状态会在应用程序性能方面增加显著的系统开销。能想象到你可以在应用程序每一次请求的运行过程中执行更多的代码,并且有可以发出重新得到被存储的状态的网络请求。不管怎样,你的应用程序可以利用很多技术在仍旧保持想要得到的状态管理性能的前提下,减少会话状态的性能冲突。
会话状态处理的批量出现在每一次请求驻留在SessionStateModule的时候。这个模块在请求处理程序获取通过请求信息被识别的会话的状态之前执行一次,在请求处理程序产生一个新的会话或者保存对在处理程序执行过程中已经存在的会话的修改之后执行一次。
进程内会话状态模式是内置于状态存储模式中最快速的一种。在从请求中抽取会话ID,运行一个缓存来查找存储在内存空间中的状态词典,标记会话为已经通过来防止超时过期等系统开销方面有限制。对会话中数据的修改将直接作用于内存中的对象,而不需要任何多余的工作为了下一次请求持续状态。然而,由于进程内模式不能承受应用程序重起并且不能工作在Web形式场景,应用程序经常只能选择使用两种进程外模式中的一种。
在默认的进程外模式——数据库和状态服务器中,会话状态必须在AcquireRequestState场景中被从外存储器中得到并且从二进制表示形式反序列化到内存状态词典构造。在ReleaseRequestState场景中,状态词典需要被再次序列化,并被传递到外存储器中。另外,存储器中的会话入口必须被更新来显示最后一次通过时间,防止超时过期。在这些模式中,状态数据的序列化和反序列化以及进程外传输,是到目前为止被会话状态介绍到的向请求路径的最昂贵的运行。
无论任何可能情况下,SessionStateModule都将默认地执行许多优化来避免系统开销。这些优化分为四类。
第一, 对于没有标记为需要会话状态的处理程序或者页面,除了在存储器中标记会话为已访问以外不会执行任何会话状态工作。对于标记为会话状态只读访问的页面,只有标记为最后访问和获取原始数据的操作会被执行。
第二, 在所有的数据都保存到了会话词典之前,还没有指定会话ID的请求的会话将不会被开始。
第三, 对于包含不可改变基元类型的会话变量没有产生会话状态访问或者只产生了会话状态读取访问的请求,对于提供程序在请求的最后将不会持续状态的改变;对于可变会话数据被通过访问或者会话被修改的请求,只有可访问的变量将会被序列化,而其他变量只是从二进制表现形式中拷贝出来。
第四, 基元类型被直接序列化,但是对象类型通过相对较慢的BinaryFormatter序列方法来序列化。
通过利用文章中带有插图的最好的实践,你可以减少会话状态管理的性能冲突。在这里讨论的策略是基于在使用会话状态时改善应用程序性能的三种最有前途的方法:
完全禁用所有可能的会话状态来避免系统开销。减少状态数据进行序列化和反序列化的系统开销。减少会话状态在进程外与状态存储器之间传输的系统开销。