高性能ASP.NET应用程序(2)——ASP.NET缓存简介

上次我们介绍了ASP.NET应用程序状态管理方面的一些内容。今天,我们将继续上次关于编写高性能ASP.NET程序的话题,探讨一下ASP.NET的缓存机制。在编写ASP.NET应用程序时,能否有效地利用缓存对于程序的响应时间和资源管理方面至关重要。这一次,我们将讨论以下四个部分:

    1)正确地使用缓存API

    2)利用输出缓存和片段缓存

    3)缓存在Web Farm中的应用

    4)有效应用缓存方面的一些建议

    缓存API非常简单并易于使用,但仍然有一些情况下,你应该避免使用它们。例如,如果你要缓存的数据是特定于某个用户的,那么还是保存在会话状态中为好。如果某些数据更新非常频繁,那么这些数据在缓存中也不会被保存很久,因此缓存这样的数据对性能提高没有任何好处。我想提请您特别注意一下的是AddValidationCallback。这个函数在当你想为不同的客户定制缓存数据时是非常有用的。下面的代码演示了如何使用AddValidationCallback来依据输入的查询字符串自定义请求的处理过程的。

<script language="c#" runat="server">

 

   HttpContext ctx = HttpContext.Current;

   static string validationstate;

 

   public void Page_Load()

   {

      Response.Cache.AddValidationCallback(new HttpCacheValidateHandler(ValidateCache), null);

      ctx.Response.Write("");

   }

 

   public static void ValidateCache(HttpContext context, Object data, ref HttpValidationStatus status)

   {

      if (context.Request.QueryString["Customer"] != "Bob")

      {

         status = HttpValidationStatus.IgnoreThisRequest;

         context.Response.Write("You are not Bob. Your request will be handled explicitly.");

      }

      else

      {

         status = HttpValidationStatus.Valid;

         context.Response.Write("Respond with cached data");

      }

   }

 

</script>

 

    有三种缓存过期策略可供选择,这使得您可以更有效的利用缓存机制。绝对过期策略,这种策略会指定一个绝对的日期和时间,某个对象一旦过期便会被从缓存中删除;滑动过期策略,如果某个对象在超过指定的时间间隔之后仍未被试用,那么它便会过期并从缓存中删除;依赖过期策略,缓存的对象可能被关联到某个资源,那么只有在这个关联的资源发生变化时才会导致对象过期。例如一个对象关联了磁盘上的某个文件,那么该对象将保留在缓存中,一直到该文件内容发生变化时为止。有一点需要注意的是,你不能将绝对过期策略和滑动过期策略一起使用,而虽然你可以将依赖过期策略和绝对或者滑动过期策略一起使用,但我们并不推荐这样做。

    输出缓存是一种强大的技术,通过缓存从动态页面生成的内容该技术能够显著提高程序的请求/响应吞吐量。它使得您可以将整个页面的内容缓存一段指定的时间。因此当动态生成的网页不包含用户特定的数据时请考虑到使用输出缓存。另外应该考虑为生成起来比较耗时但有经常访问的内容启用输出缓存,例如报表。比如生成一个报表需要花去你1分钟时间,而它由仅包含少量的变化,那么这个报表便是一个很好的缓存候选。但是,应该避免在下列这些情况下使用输出缓存:

    1.您需要以编程方式访问页面上的数据。使用缓存API。

    2.该网页包含大量的变量。

    3.该网页包含混合类型的数据:静态的,动态的还有用户特定的。在这种情况下,使用片段缓存机制为好。

    4.每次请求都会更新的网页。

    片段缓存机制可以通过使用用户控件配合@OutputCache指令来实现。当一个页面中的混合有不同种类的数据时,应该通过用户控件来将你的页面划分为若干个独立的逻辑模块。一些适宜采用片段缓存机制的情形包括:非用户特定的导航菜单,以及页眉/页脚这种通常不需要在每个视图刷新的静态内容。

    同会话状态管理类似,缓存的管理,在Web Farm的情形下也变得比单独服务器时更加复杂。基本上,你有三种方案可供选择:

    1. 在Web Farm同步所有节点。这种方式简单,快捷,优雅。我们只需简单的为程序增加一些处理逻辑,使得当任何缓存操作(API)发生时我们只是通过遍历节点并把Web请求转发到每一个节来同步所有节点上的缓存。如果它不是一个简单对象,你必须考虑到串行化的问题。

    2. 集中式缓存。你可以通过Web服务组件来设计一个集中式的缓存:1)确定什么样的信息需要被缓存。例如,假设你的应用程序生成了一个大型数据集集合,用于为每个用户提供相同的首页2)创建一个Web服务,其中包含一个生成这个大数据集的方法。3)编译此Web服务和并安装到Web Farm可以访问的一个服务器上。理想的情况下,这个服务器应该在同一个域,并在同一物理网络,以尽量减少通信时间。4)在你的程序中代码添加一个Web引用到以从此Web服务中获得这个大数据集。

    3. SQL服务器缓存。SQL Server是强大的,尤其是当你需要应对跨进程的持久性数据回收,服务器重新启动,电源故障/系统崩溃这样的事情时。如果你的应用程序需要这样的数据保护,那么基于例如SQL Server这样的持久性数据存储的缓存机制应该足以满足的需求了。

    当您编写ASP.NET应用程序时,请务必记住牢记以下一些规则:

    1. 使用用户控件来分割您的网页。隔离动态数据和静态数据,以便能够应用片段缓存。

    2. 配置内存限制,以避免造成任何内存问题。参考:https://msdn.microsoft.com/en-us/library/7w2sway1(VS.71)aspx。

    3. 使用Location属性选择合适的位置来缓存你的数据。 https://msdn.microsoft.com/en-us/library/system.web.ui.outputcachelocation(v=VS.71).aspx

    4. 使用VaryBy。VaryBy属性决定了数据是否被缓存。下面的示例显示了如何使用VaryBy属性。

    <%@ OutputCache Duration="30" VaryByParam="a" %>

 

    在上面示例中所示的设置使得以下页面具有相同的缓存版本:

    如果您添加b到VaryByParam属性,你将有三个不同版本的网页,而不是一个版本。

    当你决定使用VaryBy属性时,确保有一个有限数量的变化,因为每个变化都增加了对Web服务器上的内存消耗。

    以上就是一些我想分享的关于ASP.NET缓存的经验。根据我的经验的基础上,即使有经验的.NET开发人员也经常会在ASP.NET缓存方面失误,比如由于缓存太频繁造成的内存问题,缓存了本不应该被缓存的东西而引起的性能问题.还好我们有一些诊断/调试工具来帮助我们找出真正的罪魁祸首。但是,如果我们谨慎设计我们的缓存代码,应该就不至于那么痛苦的调程序了。

Yawei