性能对于任何应用或者你研发的代码来说都是一个关键需求。浏览器通过缓存文本和图像来提高性能,而服务器端你选择的缓存实现对于提供最好的性能来说是至关重要的。缓存是这样一个过程,将频繁使用的数据存储到服务器上来满足接下来的请求。你会发现从内存中抓取对象比重新生成网页和每一次从头生成网页中包含的items要来的快得多。缓存提高了你的应用的性能。你将你的缓存方法调配到越好,你的应用性能就越好。
这一章聚焦于缓存,包括AN提供的SQL cache-invalidation功能。
CACHING
在AN中你有一些办法来处理caching,其中之一就是你可以缓存一个完整的HTTP响应,这个机制就是output caching。另外的两个方法是部分网页缓存和数据缓存。
Output Caching
输出缓存是将动态产生的网页内容保存在服务器的内存或者硬盘上,用以后续的获取。这种类型的缓存将渲染后的内容保存了,如果下一次请求的时候就不需要重新生成了。当一个网页缓存后,后续的请求可以使用。你通过插入一个OutputCache网页指令来应用输出缓存。
<%@ OutputCache Duration="60" VaryByParam="None" %>
这个Duration属性定义了网页缓存的时间。这个VaryByParam属性决定了网页的输出的哪个版本被缓存。AN中的缓存是由一个HttpModule来实现的,这个HttpModule侦听所有HttpRequests。这个OutputCacheModule侦听应用的ResolveRequestCache和UpdateRequestCache事件。
VaryByParam
这个属性制订了QueryString的哪个参数引起网页缓存的新版本
<%@ OutputCache Duration="90" VaryByParam="pageId;subPageId" %>
如果你想要缓存网页的新版本,基于QueryString参数的改变。使用下面的格式
<%@ OutputCache Duration="90" VaryByParam="*" %>
当使用VaryBy参数的时候做一些计算是非常重要的,你可以加上VaryByHeader然后根据浏览器报道的User-Agent Http header缓存新的版本。
<%@ OutputCache Duration="90" VaryByParam="*" VaryByHeader="User-Agent"%>
这个User-Agent验证了浏览器的类型。AN可以自动产生对应不同浏览器的同一网页的不同渲染,所以在很多情况下,在缓存中保存这些不同的渲染很有意义,火狐用户相比IE用户可能会有不同的HTML。所以你不会想要将渲染后的HTML发给所有的用户。
Warning 永远缓存那些可以带给你最大性能增益的东东,用测试来证明假设,不要使用VaryByParam=”*”。一个通常的规则是缓存一些然后需要的时候增加缓存。记住,服务器端的内存是有限资源,所以在一些情况下你可能想要配置硬盘缓存。确保平衡资源有限和安全。不要将敏感数据放到硬盘上。
VaryByControl
VaryByControl可以应用于那些不怎么改变的UserControl,它们渲染的HTML不怎么改变,能够得到性能增益。
VaryByCustom
尽管这个VaryBy属性提供了很多能力,有些时候你需要更多的灵活性。
Extending
从AN4开始,你可以扩展OutputCache指令。
Partial Page(UserControl)Caching
和output caching类似,partial page caching允许你缓存一个网页的部分。你可以,举个例子,只缓存用户看得到的网页中心。
通常,用户控件被放置在多个网页,来最大化通用功能的复用。然而,当这些用户控件以@OutputCache指令缓存的时候,它们是以各个网页为单位缓存的。这意味着如果一个用户空间在网页a和网页b吐出的HTML是一样的,这个用户控件的输出被缓存了两次。通过Shared=”true”属性的设置,用户控件的输出可以在多个网页中和站中共享。
<%@ OutputCache Duration="300" VarByParam="*" Shared="true"%>
因为渲染后的用户控件只缓存了一次,而不是每一个网页都缓存了,所以节省的内存非常可观。和所有优化一样,你需要测试输出的正确性和内存的使用。
警告 如果你有一个ASCX 用户控件使用OutputCache指令,记住这个用户控件只在第一个请求中存在。如果一个用户控件的HTML是从OutputCache中获取的,这个控件在ASPX网页中不存在。一个PartialCachingControl被创建类似于控件的灵魂。
ASPX中任何代码如果要求一个用户控件一直存在,都会因为该控件从OutputCache中重构而失败。确保要对这种缓存类型的控件进行校验
protected void Page_Load() {
if (PossiblyCachedUserControl != null) {
// Place code manipulating PossiblyCachedUserControl here.
}
}
Post-Cache Substitution
整个网页的输出被缓存作为后续之用。然而,通常你希望鱼与熊掌兼得,你既想要有输出缓存的增益,又想要在网页保持一点动态内容。如果缓存了一个网页但是不能够输出一点动态内容,这糗太大了。
从AN2.0开始,一个所谓post-cache substitution的手段被视为可以影响即将渲染的网页的途径。它会在cache内容返回的时候调用你指定的方法。这个方法返回你喜欢的任何字符串,但是你不要滥用这个特色。如果你的post-cache substitution代码调用了一个代价昂贵的过程,你很容易的就失去了你期望的性能增益。
Post-cache substitution是一个非常容易使用的特色。它给了你两种方法来控制这个替换。
- 调用Response.WriteSubstitution方法
- 在网页增加控件,将它的methodName属性设为回调方法的名字。
HttpCachePolicy and Client-Side Caching
Caching在服务器端不仅仅是将数据保持在内存中。一个好的caching策略同样应该包含浏览器和它的客户端缓存,这是由HTTP header控制的。HTTP headers是指导浏览器如何处理请求的指令。
有人建议用HTML的meta标签来控制缓存行为。注意浏览器和路由都没有责任注意这些指令,还是使用HTTP头比较实用。
因为HTTP头在HTTP消息的body外,你可以有一些观察它们的选择。你可以启用tracing来观察这些头。你可以使用另外的软件,比如你的浏览器网页开发者插件,或者非常流行的火狐扩展Firebug。
观察AN使用的默认HTTP头,注意Cache-Control:private,指示路由和其他的中介这个响应只给你。
HttpCachePolicy类给你了一个对对象模型,来管理客户端的状态,不用你亲自改变HTTP的头。将下面的代码加到Page_Load中来影响响应的头和浏览器的cache行为。这一段代码告诉浏览器不要cache响应在内存或者存储在硬盘上,它也指示这个响应马上就截止没用了。
protected void Page_Load(object sender,EventArgs e){
Response.Cache.SetCacheability(HttpCachePolicy.NoCache);
Response.Cache.SetNoStore();
Response.Cache.SetExpires(DateTime.MinValue);
Response.Write(DateTime.Now.ToLongTimeString());
}
和不加这段代码进行比较。两个HTTP头指示客户端浏览器,Cache-Control头变为了no-cache,no-store。
如果你的AN英语哦那个包含了许多相对静止和非时效的网页,考虑考虑客户端的缓存策略。利用用户强大客户机上的硬盘控件和内存是一个更明智的选择。
CACHING PROGRAMMATICALLY
用户控件和网页可以用OutputCache指令标记,使你的网站的行为发生天翻地覆的变化。
Data Caching Using the Cache Object
caching的另一个方法是使用System.Web.Caching.Cache对象来开始缓存具体的数据以备一个或者一群网页使用。这个Cache对象允许你存储任何东西,小到name value对,大到复杂的对象,类似数据集或者整个.aspx网页
警告 尽管它和session state很类似,这个Cache对象是网页服务器的应用的所有用户共享的。所以如果你在cache里放了一个特殊的item,所有的用户可以看到这个对象。这可能在服务器农场里不适用,因为你不能确定为用户提供服务的服务器是哪一个,即使只有一个服务器。除此之外,服务器在需要收回内存的情况下,可以在任何时刻使任何缓存的item无效。
你可以以下面这种方式使用Cache
Cache["WhatINeedToStore"]=myDataSet;
当一个在cache中时,你可以在后面的过程中获取
DataSet ds=new DataSet();
ds=(DataSet)Cache["WhatINeedToStore];
使用Cache对象来缓存你的网页是非常棒的方法,事实上,OutputCache指令使用的其实就是Cache对象。这块小部分显示了Cache对象的简单用法,然而,Cache的真正强大之处在于它有自毁的能力。这就是缓存依赖的由来。
警告 你必须常常遵循检测模式来查看一个item是否在Cache对象中,如果不在,你需要尽力来回复这个对象。当它重建后,你可以又一次把它插入到cache中使其在接下来的请求中可以访问。
Controlling the ASP.NET Cache
通常,AN为Caching子系统设置的默认参数对大多数场景都是适用的。它们可以被配置,在machine.config或者web.config文件中。这些选择可以让你改变缓存的行为,比如当系统在内存压力下使得缓存items都截止。你可以设置应用的私有字节阈值,超过这个值,cache开始冲洗items。
<system.web>
<cache disableMemoryCollection="false" disableExpiration="false" privateBytesLimit="0" percentagePhysicalMemoryUsedLimit="90" privateBytesPollTime="00:02:00" />
Cache Dependencies
使用Cache对象,你可以基于不同的依赖存储和废弃items。在AN1.0/1.1,仅有的几个依赖是
- 基于文件的依赖
- 基于key的依赖
- 基于时间的依赖
当你使用Cache对象插入items时,你使用Insert方法设置依赖。
Cache.Insert("DSN", connectionString,
New CacheDependency(Server.MapPath("~/myconfig.xml")))
从AN2.0开始,缓存依赖通过AggregateCacheDependency类的引入被提升了。
The AggregateCacheDependency Class
这个类类似于CacheDependency类,但是它允许你在缓存中的item和许多不同类型的不同依赖间建立联系。例如,如果你有一个从XML中构建的数据item存储在Cache 中,而且你有从SQL 数据库表中来的信息。你可以创建一个AggregateCacheDependency,用CacheDependency对象模拟子依赖。
var agg=new AggregateCacheDependency();
agg.Insert(new CacheDependency(Server.MapPath("~/myconfig.xml")));
agg.Insert(new SqlCacheDependency("Northwind","Customers"));
Cache.Insert("DNS",connectionString,agg);
注意AggregateCacheDependency是被不同种类的CacheDependency使用的。如果你只是想要将一个缓存的item与多个文件联系起来,使用CacheDependency的重载。
Cache.Insert("DNS",yourObject,new System.Web.Caching.CacheDependency(new string[]{Server.MapPath("~/file1.xml"),Server.MapPath("~/file2.xml")}))
The Unsealed CacheDependency Class
从AN2.0发布后,caching中最大的变化就是CacheDependency类变成了重构和未证实的。这允许你的可以制造从CachDependency继承的类,并且创建更多精细的依赖,这些依赖不单单局限于Time,Key或者File依赖。
当你创建你自己的缓存依赖时,你有增加过程的选项,例如网页服务数据,只在午夜的依赖,或者文件内字符串的变化。你创建的依赖只受限于你的想像。CacheDependency的未证实特性将你放在了能够决定Cache对象中缓存的数据何时无效的位置上。
AN队伍也构造了一个SQL Server cache 依赖-SqlCacheDependency。当SQL Server中的一个表改变时,cache无效,你现在可以在的AN应用中马上得知这个变化。
因为CacheDependency是未证实的,你可以获得你自己定制的cache依赖。
Creating Custom Cache Dependencies
AN有基于时间,基于文件和基于SQL的CacheDependency支持。你应该问问自己,你为什么要写一个CacheDependency。
- 使得从一个目录查询结果无效。
- 当一个MSMQ或者MQSeries消息来临时 使得缓存无效
- 创建一个Oracle有关的CacheDependency
- 当XML网页服务数据返回时,使得cache无效
- 从一个股票服务返回新数据时,更新缓存内的数据
CacheDependency类公开了三个members和一个构造重载供开发者利用
- GetUniqueID 允许你返回这个定制缓存依赖的唯一标示符号
- DependencyDispose 用来处理这个定制缓存依赖使用的资源,当你创造一个缓存依赖的时候,你必须实现这个方法
- NotifyDependencyChanged 缓存item截止
- CacheDependency 公共的构造函数
.NET 4.x’s New Object Caching Option
截至目前,你可以看到System.Web.Caching.Cache对象是多么强大,甚至允许你创建一个定制的cache。
USING THE SQL SERVER CACHE DEPENDENCT
为了利用AN中的SQL Server Cache Dependency特色,你需要在你的SQL Server数据库中执行一步。为了建立你的SQL Server使用aspnet_regsql.exe工具。这个工具对SQL Server做出需要的修改,你可以开始体验SQL cache-invalidation的特色。
当你使用SQL Server Cache Dependency特色时,使用下面的步骤
- 将你的数据库设置为支持SQL Cache Dependency
- 将你的数据库中的一张或者多张表设置为支持SQL Cache Dependency
- 将SQL 连接字符串包含在web.config文件中
- 下面阐述了几种使用SQL Cache Dependency特色的方法
- 在代码中生成一个SqlCacheDependency。
- 在OutputCache指令中添加一个SqlDependency属性。
- 在Response对象中添加一个SqlCacheDependency实例。
CONFIGURATION YOUR ASP.NET APPLICATION
当你对数据库和数据库中的表启用SQL Server cache invalidation后,下一步就是对你的应用进行配置。
为了配置你的应用来和SQL Server cache invalidation合作,第一步就是对你的web.config做出调整。在这个web.config文件中,指明你想要和哪个数据库打交道,你想要AN和其连接。
下面的代码给出了例子,你怎么样改变你的web.config文件和SQL Server cache invailidation打交道。如果你使用的时SQL Server2005以上的,这个pollTime属性不是必须的,因为使用的是数据库的通知事件。
<configuration>
<connectionStrings>
<add name="AppConnectionString1" connectionString="Data Source=localhost;User ID=username;Password=password;Database=Northwind;Persist Security Info=False"providerName="System.Data.SqlClient" />
</connectionStrings>
<system.web>
<caching>
<sqlCacheDependency enabled="true">
<databases>
<add name="Northwind" connectionStringName="AppConnectionString1"
pollTime="500" />
</databases>
</sqlCacheDependency>
</caching>
</system.web>
</configuration>
现在你的web.config已经成功建立,你可以开始使用SQL Server cache invalidation在你的网页上。
Attaching SQL Server Cache Dependencies to the Request Object
除了改变OutputCache指令的设置来激活SQL Server Cache Invalidation,你同样可以通过代码来设置SQL Server Cache invalidation。你通过SqlCacheDependency类来完成。
SqlCacheDependency myDependency = new SqlCacheDependency("Northwind", "Customers"); Response.AddCacheDependency(myDependency); Response.Cache.SetValidUntilExpires(true); Response.Cache.SetExpires(DateTime.Now.AddMinutes(60)); Response.Cache.SetCacheability(HttpCacheability.Public);
Attaching SQL Server Cache Dependencies to the Cache Object
除了将SQL Server缓存依赖添加到请求对象,你可以将它们添加到Cache对象。Cache对象包含在System.Web.Caching命名空间,它使得你可以缓存任何类型的数据。下面的例子演示了如何利用SqlCacheDependency对象和Cache对象。
SUMMARY
AN提供了几种内置的缓存机制。这些特色大多数可以通过属性设置得到,所以没有实际代码需要。理解不同类型的缓存非常重要,包括部分网页缓存和缓存后的替换。
当和数据库打交道的时候,SS cache invalidation特色是AN的一个亮点,允许你在表发生变化的时候使得缓存里的items无效。缓存后替换提供了构建动态网站又有缓存性能的可能。
当你监听数据库中的变化时,你可以很容易的通过web.config文件来配置这些过程,或者你也可以在你的代码中完成这些操作。