摘 要: 介绍了ASP.NET中各种缓存技术的应用程序模型,并重点分析了每种应用模型的优缺点及其适用范围。
关键词: ASP.NET 高速缓存 Cookie 会话状态 应用程序状态 视图状态
随着经济全球化的发展,许多公司面对客户和市场需求的变化,需要构建足够强大、灵活、可伸缩的信息系统来支撑迅速增加的大量无规律性的访问和Web流量。
要开发高性能、可缩放的 Web 应用程序,最好的方法是在首次请求项时将这些项存储在Web服务器上或请求流中的其他软件上,从而避免重新创建满足先前请求的信息,可节省时间和资源。这就要使用数据缓存功能。ASP.NET提供了许多缓存功能。为了知道应用程序应该用哪一种缓存功能,需要了解将要开发的应用程序的结构和每种缓存功能的工作特点及其优缺点。作者在软件开发过程中,积累了有关这方面的一些经验,现整理出来供大家参考。
1 缓存数据的分类
在Web应用程序中,由于变量的生命周期受限于网页,所以,每当.aspx文件被解释执行后,变量的内容将不复存在。为将变量的内容保存下来,简单的方法是使用缓存来存储变量的值。ASP.NET提供了输出缓存和应用程序数据缓存二种方案,用来创建高性能Web应用程序的缓存类型[2]。
缓存数据按其所在网络节点位置的不同可以分为三类[1][6]:(1)在客户端缓存数据。(2)在Web服务器上维护缓存数据。(3)在数据库中维护缓存数据。它是用数据库储存Web应用程序的全局或会话数据的一种方式。
2 各种缓存技术的应用模型
2.1 无状态方式——不维护缓存状态
在软件开发中,并非必须使用缓存来保存数据,完全可以通过Web应用程序的ASP.NET代码执行查询并用绑定控件将结果转换为HTML,从而避免缓存数据,使ASP.NET代码完全是无状态的。
(1)优点:由于不需要在ASP.NET代码中缓存数据,所以应用程序的每位用户需要的内存是最小的。如果单纯从内存使用的角度看,这种方式最具有伸缩性。
(2)缺点:应用程序的性能差。假设应用程序是一个在线目录,则需要在所有页上显示产品目录列表。如果每次用户访问页时都对数据库进行查询以得到产品目录的列表,则将浪费宝贵的数据库连接资源,降低系统的性能。
2.2 在客户端缓存数据
这类方法通常是利用浏览器(如Explorer)本身固有的缓存机制来实现的。浏览器把一定时间内用户访问过的文档或数据都存储起来,当用户重新访问同样的信息时就可以从浏览器的缓存中取出,而不需要重新建立HTTP连接。但是,由于必须将信息发送到客户端进行存储,因此这种方式可以存储的信息量存在一定的客观限制。
2.2.1 Cookie
Cookie 用于在客户端上存储少量经常更改的信息。许多Web站点用Cookie在客户端储存用户信息,这些信息与请求一起发送到服务器。ASP.NET的Request和Response对象都公开一个Cookies集合,可以用它在服务器和客户端往返行程之间储存和获取信息。
(1)优点:①简单。Cookie是具有简单键值对的、轻量的、基于文本的结构。②可伸缩性好。不需要任何服务器资源,在客户端储存数据使ASP.NET代码成为无状态的,这可改进可伸缩性。③可配置到期时间。可以通过设置HttpCookie对象的Expires属性,控制Cookie的失效时间。
(2)缺点:①储存量大小受到限制。对于缓存返回许多行的查询结果并不适用。②与浏览器安全设置有关。在浏览器的安全设置中,可以选择允许或拒绝创建Cookie,如果浏览器选择禁止接受任何Cookie,则拒绝用Cookie储存用户的数据。③安全性差。由于用户可以修改在客户端储存的Cookie内容,所以不可用来存储像密码之类的敏感信息。④持久性。客户端计算机上Cookie的持久性受到客户端Cookie到期进程以及用户干预的制约。
2.2.2 隐藏域
可以在页上的隐藏域中存储特定于页的信息,方法类似于Windows窗体中的隐藏控件。ASP.NET提供HtmlInputHidden控件,该控件提供隐藏域功能。但最好使用同它功能类似的ViewState。
(1)优点:①简单。②可伸缩性好。无需服务器资源,隐藏域在页上存储并读取。③与浏览器安全设置无关。几乎所有浏览器和客户端设备都支持具有隐藏域的窗体。
(2)缺点:①有限的存储结构。隐藏域不支持丰富的结构,隐藏域提供在其中放置信息的单值域。②安全性差。如果直接查看页输出源,可以看到隐藏域中的信息,这导致潜在的安全性问题。③性能降低。由于隐藏域存储在页本身,因此,如果存储较大的值,则在用户显示页和发送页时,页的速度就可能会减慢。
2.2.3 视图状态(ViewState)
在System.Web.UI名称空间的Page类公开了一个ViewState属性,它包含一个类似于名称/值对集合的StateBag对象。ASP.NET页框架将ViewState存储到一个字符串变量,将它发送到客户端并作为隐藏变量返回。在回发时,页框架分析来自隐藏变量的输入字符串并填充每一控件的ViewState属性。
(1)优点:①可伸缩性好。②与浏览器安全设置无关。ViewState是在页代码内结构中的一个隐藏域中维护,所以,不管浏览器的安全设置如何,都可以用页面的ViewState来储存和获取数据。③储存量大。页面可以在ViewState中保存大项目。④页和控件状态自动保持。
(2)缺点:①安全性较低。虽然储存在ViewState中的数据是散列的、压缩的并且是为Unicode实现而编码的,但用户仍然可以解密储存在页面上的值。②性能降低。如果存储较大的值,在用户显示页和发送页时,页的速度就可能会减慢。③储存数据的数据类型受限制。在ViewState中,只能储存ASP.NET代码清楚如何序列化的数据,如字符串和整数这样的简单数据类型,但不能储存一般对象。尽管如此,它仍然支持ISerializable接口的对象,如Dataset对象。
2.3 在Web服务器上维护缓存数据
这类方法是在Web服务器上使用内存作为缓存空间[6]。当服务器响应用户对某个数据请求后,在内存缓存空间中保留一个副本,下一次如果有相同的访问请求,就直接将缓存空间中的副本提供给用户。这可加快服务器的反应能力和增加服务器的吞吐量。服务器端的缓存机制能够减小用户的访问延迟,与客户端缓存相比具有更高的安全性。但可能使用更多的Web服务器资源,增加了服务器硬件和软件的复杂度,当在信息存储量较大时限制了应用程序的可扩展性。
ASP.NET提供了在服务器上维护状态管理的四种方式。
2.3.1 会话状态(Session)
Page类公开一个Session属性,可以在Session中存储会话特定的值和对象,该Session对象将由服务器来进行管理并可用于浏览器或客户端设备。存储在Session变量中的理想数据是特定于单独会话的短期的、敏感的数据,直到用户的会话中止。
对Session的应用,要先通过在文件Global.asax中进行设置,然后在.aspx页面中进行调用。如可用下面的程序在Global.asax中加上一个Session_Start方法以定义要响应的属性。
void Session_Start(Object sender,EventArgs e)
{
Session.Add(″lastvalue″,0);
}
赋予属性一个初值,这样当在页面(.aspx)装入时直接使用这个默认值。对Session值的应用类似于对变量的使用(如Position=(int)Session[″lastvalue″]+1;),如果用它存储数据集,则必须将其从Object强制转换回数据集。
(1)优点:①易于实现。Session为ASP开发人员所熟悉,并且与其他.NET框架类一致。②安全性高。数据是由Web服务器而不是在浏览器中维护的,用户不能通过浏览器来访问或修改Session中的内容。③与浏览器安全设置无关。Session可用于不支持HTTP Cookie的浏览器。④独立性好。每个用户的会话设置是分开的,不可共享。⑤会话特定的事件。会话管理事件可以由应用程序引发和使用。⑥持久性。放置于会话状态变量中的数据在经过Internet信息服务(IIS)重新启动和辅助进程重新启动后,会话数据不会丢失。⑦平台可缩放性。会话状态对象可在多计算机和多进程配置中使用,因而优化了可缩放性方案。
(2)缺点:①可伸缩性差。应用程序的每个会话都要在服务器上占用一定的资源,当有大量访问Web应用程序的用户时,就应考虑要使用的服务器资源总量。②性能降低。Session变量在被移除或替换前保留在内存中,如果其中包含类似大型数据集的信息块,则可能会因服务器负荷的增加而降低Web服务器的性能。
2.3.2 应用程序状态(Application)
ASP.NET通过HttpApplicationState类将Application提供为一种存储全局应用程序特定信息的方法。Application对象与Session对象类似,不同的是通过页面的Application属性取得的数据是所有会话所共享的。插入到Application变量的理想数据是那些由多个会话共享并且不经常更改的数据。
(1)优点:①易于实现。②与浏览器安全设置无关。③共享性高。因为储存在Application中的数据对应用程序的所有会话都是可用的,所以它是储存所有会话都能使用的稳定性数据(如产品目录列表)的理想位置。
(2)缺点:①可伸缩性差。②持久性。因为在Application中存储的全局数据是易失的,所以如果包含这些数据的 Web服务器进程被损坏,将丢失这些数据。③全局范围。在Application中存储的变量仅对于该应用程序正在其中运行的特定进程而言是全局的,并且每一应用程序进程可能具有不同的值。因此,不能依赖Application来存储惟一值或更新网络园和网络场配置中的全局计数器。④性能降低。当服务器负载增加时,就会降低Web服务器的性能,但比在Session中维护数据的情况要好。
2.3.3 Cache
Page对象公开一个Cache属性,可以将Cache对象想象为更健壮的Application对象。储存在Cache中的数据对应用程序的所有会话都是可用的。可以像对Application对象一样访问和修改Cache对象的内容[3]。
但在通过Add或者Insert方法向Cache添加项目时,Cache对象还提供了下面的附加功能:
(1)可以通过提供具体值(DateTime)或者相对值(Time-
Span)来指定何时从缓存中删除该项。
(2)可以指定CacheDependancy,它会在dependent项改变时迫使项从缓存中删除。
(3)可以指定当项从缓存中被删除时,ASP.NET要调用的回调函数。
使用Cache对象的优点及缺点与Application对象的很相似,只是Cache对象还提供了额外的缓存删除功能。
下面给出一个使用Cache存储大数据的示例。在下面的Web表单中,对第一次请求从数据库中检索数据,并把它存入到应用程序缓存。将Dataset加入缓存时,设置5分钟的到期限制,以便5分钟内的全部请求都使用缓存的Dataset。对于缓存Dataset到期之后出现的第一次请求,数据库又会被访问,新的Dataset开始缓存。为了证实这一观点,Dataset第一次被创建,就把“键—值”对加入到DataSet.ExtendedProperties集合中,这是一个能保存用户定义的“键—值”对集合。用它可存储Dataset生成时间,这样就可把它显示到页面上。
下面的方法用于在Cache中存储数据并在控件DataGrid1中显示。
private void datasetshow( )
{/**构建新的DataSet,并用Cache的Get的方法的结果为它赋值,该方法返回匹配指定键名的Object实例,并强制将它转化为一个DataSet对象*/
DataSet myDataSet=(DataSet) Cache.Get(″CacheddataSet″);
if (myDataSet==null)
{//dataLocationLabel记录DataSet所在的位置及时间
dataLocationLabel.Text=″<p>第一次来!时间是:″+
DateTime.Now.ToLongTimeString( )+″</p>″;
myDataSet=new DataSet( );
//登录数据库服务器并填充数据集
login(″sa″,myDataSet);
//记录创建时间
myDataSet.ExtendedProperties.Add(″creattime″,
DateTime.Now.ToLongTimeString( ));
//在Cache中插入一个DataSet对象,设置5分钟到期限制Cache.Insert(″CacheddataSet″,myDataSet,null,DateTime.Now.AddMinutes(5),TimeSpan.Zero);
}
else {
dataLocationLabel.Text=″<p>数据取自cache中,创建时间是:″+myDataSet.ExtendedProperties[″creattime″]+″</p>″+″<p>你这次来的时间是:″+DateTime.Now.ToLongTimeString( )+″</p>″;
}
DataGrid1.DataSource=myDataSet.Tables[″authors″];
DataGrid1.DataBind( );
}
2.3.4 输出缓存
输出缓存允许将动态页或用户控件响应存储在输出流(从发起服务器到请求浏览器)中任何具备HTTP 1.1缓存功能的设备上。当后面的请求发生时,不执行页或用户控件代码,缓存的输出用于满足该请求。
(1)优点:如果需要根据DataSet的内容重复产生同样的HTML,则输出缓存就是最有效的,因为它在服务器端需要的处理更少。与Cache类似,可以控制输出在缓存中保留的时间。
(2)缺点:就像其他服务器端缓存功能一样,输出缓存仍然会消耗服务器上的资源。
2.4 在数据库中维护状态
当应用程序执行一个非常复杂或者相当花费时间的查询时,可以将查询的结果储存在数据库服务器上的一个单独数据表中,而不是每次在用户请求下一页的结果时都要执行这个查询[1]。
(1)优点:①无状态性。②持久性。可以根据需要在尽可能长的时间内存储数据库信息,这些信息不受Web服务器可用性的影响。③安全性高。对数据库的访问要求严格的身份验证和授权,这种访问通常非常安全。④可伸缩性好、应用程序的性能高。使用这种方法即使数据量很大,对应用程序的性能影响也不会大。⑤可靠性和数据完整性。数据库包括多种用于维护有效的功能,其中包括触发器和引用完整性、事务等。通过在数据库中(而不是在会话状态等对象中)保存有关事务的信息,可方便地从错误恢复。⑥可访问性。存储在数据库中的数据可供多种信息处理工具访问。
(2)缺点:①复杂性。使用数据库支持状态管理意味着更复杂的硬件和软件配置,这比将数据储存在Session、Application、Cache、ViewState或者Cookie这样的简单对象或集合中更复杂。②性能降低。不佳的关系数据模型结构可能导致扩展问题,对数据库执行过多的查询可能会影响服务器性能。
3 结束语
综上所述,对于安全性要求高的应用程序,应当使用在Web服务器上维护缓存数据的方式;伸缩性要求高的应用程序,应当使用在客户端上维护缓存数据的方式。储存的数据越多,加载页面的时间越长,系统的性能就差。如果需要储存大量特定于会话的数据,在数据库中储存数据是一种明智的选择。
参考文献
1 Sceppa D著,梁超,张莉,贺堃译.ADO.NET技术内幕.北京:清华大学出版社,2003
2 McClure W B,Croft J J著,李万红译.构建高度可伸缩的.NET数据库应用程序.北京:清华大学出版社,2003
3 Dickinson P著,张晓明译.ADO.NET高级编程.北京:中国电力出版社,2003
4 刘杨.突破C#编程实例五十讲.北京:中国水利水电出版社,2002
5 陈英学.Microsoft ASP.NET深入编程.北京:北京希望电子出版社,2001
6 乐德广.网络缓存技术及应用的研究.计算机系统应用,2003;(5)
7 李用江.数据库中大文本数据存取方法的探讨.计算机系统应用,2004;(5)