ASP.NET MVC缓存
根据缓存的位置不同,可以区分为:
①客户端缓存(缓存在用户的客户端,例如浏览器中)
②服务器缓存(缓存在服务器中,可以缓存在内存中,也可以缓存在文件里,并且还可以进一步地区分为本地缓存和分布式缓存两种)
应该说,缓存的设计是一门较为复杂的学问,主要考虑的问题包括:要不要缓存?要缓存哪些数据?要缓存多少数据?要缓存多久?如何更新缓存(手动还是自动)?将缓存放在哪里?本文将以较为通俗易懂的方式,来看一看在MVC4的项目中,如何使用缓存功能。对于上述提到的一些具体业务问题,我这里不会进行太过深入地探讨。
MVC3中的缓存功能
ASP.NET MVC3 继承了ASP.NET的优良传统,内置提供了缓存功能支持。主要表现为如下几个方面:
①可以直接在Controller,Action或者ChildAction上面定义输出缓存(这个做法相当于原先的页面缓存和控件缓存功能)
②支持通过CacheProfile的方式,灵活定义缓存的设置(新功能)
③支持缓存依赖,以便当外部资源发生变化时得到通知,并且更新缓存
④支持使用缓存API,还支持一些第三方的缓存方案(例如分布式缓存)
那么,下面我们就逐一来了解一下
一、使用输出缓存
MVC中内置了一个OutputCache的ActionFilter,我们可以将它应用在某个Action或者ChildAction上面
【备注】ChildAction是MVC3的一个新概念,本质上就是一个Action,但通常都是返回一个PartialView。通常这类Action,可以加上一个ChildActionOnly的ActionFilter以标识它只能作为Child被请求,而不能直接通过地址请求。
【备注】我们确实可以在Controller级别定义输出缓存,但我不建议这么做。缓存是要经过考虑的,而不是不管三七二十一就全部缓存起来。缓存不当所造成的问题可能比没有缓存还要大。
1 [OutputCache(Duration=10)]
2 public ActionResult Index()
3 {
4 //这里目前作为演示,是直接硬编码,实际上可能是读取数据库的数据
5 var employees = new[]{
6 new Employee(){ID=1,Name="ares",Gender="Male"}
7 };
8 return View(employees);
9 }
那么,也就是说,第一次请求这个Index的时候,里面的代码会执行,并且结果会被缓存起来,然后在10秒钟内,第二个或者后续的请求,就不需要再次执行,而是直接将结果返回给用户即可。
这个OutputCache的Attribute,实际上是一个ActionFilter,它有很多参数。参考http://msdn.microsoft.com/zh-cn/library/system.web.mvc.outputcacheattribute(v=vs.118).aspx
使用缓存配置文件
第一节中,我们详细地了解了MVC中,如何通过OutputCache这个ActionFilter来设置缓存。但是,因为这些设置都是通过C#代码直接定义在Action上面的,所以未免不是很灵活,例如我们可能需要经常调整这些设置,该如何办呢?
ASP.NET 4.0中提供了一个新的机制,就是CacheProfile的功能,我们可以在配置文件中,定义所谓的Profile,然后在OutputCache这个Attribute里面可以直接使用。
通过下面的例子,可以很容易看到这种机制的好处。下面的节点定义在system.web中
1 <caching> 2 <outputCacheSettings> 3 <outputCacheProfiles> 4 <add name="employee" duration="10" enabled="true" location="ServerAndClient" varyByParam="none"/> 5 </outputCacheProfiles> 6 </outputCacheSettings> 7 </caching>
1 [OutputCache(CacheProfile="employee")] 2 public ActionResult Index() 3 { 4 //Response.Cache.SetOmitVaryStar(true); 5 ViewBag.CurrentTime = DateTime.Now.ToString(); 6 7 //这里目前作为演示,是直接硬编码,实际上可能是读取数据库的数据 8 var employees = new[]{ 9 new Employee(){ID=1,Name="ares",Gender="Male"} 10 }; 11 return View(employees); 12 }
二、使用缓存API
通过上面的两步,我们了解到了使用OutputCache,并且结合CacheProfile,可以很好地实现灵活的缓存配置。但是有的时候,我们可能还希望对缓存控制得更加精细一些。因为OutputCache是对Action的缓存,不同的Action之间是不能共享数据的,假如某些数据,我们是在不同的Action之间共享的,那么,简单地采用OutputCache来做,就会导致对同一份数据,缓存多次的问题。
所以,ASP.NET除了提供OutputCache这种基于声明的输出缓存设置之外,还允许我们在代码中,自己控制要对哪些数据进行缓存,并且提供了更多的选项。
关于如何通过API的方式添加或者使用缓存,请参考http://msdn.microsoft.com/zh-cn/library/18c1wd61%28v=VS.80%29.aspx
基本上就是使用HttpContext.Cache类型,可以完成所有的操作,而且足够灵活。
值得一提的是,我知道不少公司在项目中都会采用一些ORM框架,某些ORM框架中也允许实现缓存。例如NHibernate就提供了较为丰富的缓存功能,大致可以参考一下 http://www.cnblogs.com/RicCC/archive/2009/12/28/nhibernate-cache-internals.html
需要注意的是,微软自己提供的Entity Framework本身并没有包含缓存的功能。
这里仍然要特别提醒一下,使用这种基于API的缓存方案,需要仔细推敲每一层缓存的设置是否合理,以及更新等问题。
三、使用缓存依赖
很早之前,在ASP.NET中设计缓存的时候,我们就可以使用缓存依赖的技术。关于缓存依赖,详细的信息请参考 http://msdn.microsoft.com/zh-cn/library/ms178604.aspx
实际上,这个技术确实很有用,ASP.NET默认提供了一个SqlCacheDependency,可以通过配置,连接SQL Server数据库,当数据库的表发生变化的时候,会通知到ASP.NET,该缓存就会失效。
值得一提的是,不管是采用OutputCache这样的声明式的缓存方式,还是采用缓存API的方式,都可以使用到缓存依赖。而且使用缓存API的话,除了使用SqlCacheDependency之外,还可以使用标准的CacheDependency对象,实现对文件的依赖。
四、分布式缓存
上面提到的手段都很不错,如果应用系统不是很庞大的话,也够用了。需要注意的是,上面所提到的缓存手段,都是在Web服务器本地内存中进行缓存,这种做法的问题在于,如果我们需要做负载均衡(一般就会有多台服务器)的时候,就不可能在多台服务器之间共享到这些缓存。正因为如此,分布式缓存的概念就应运而生了。
谈到分布式缓存,目前比较受到大家认可的一个开源框架是 memcached。顾名思义,它仍然使用的是内存的缓存,只不过,它天生就是基于分布式的,它的访问都是直接通过tcp的方式,所以可以访问远程服务器,也可以多台Web服务器访问同一台缓存服务器。
需要注意的是,分布式缓存不是为了来提高性能的(这可能是一个误区),并且可以肯定的是,它的速度一定会被本地慢一些。如果你的应用只有一台服务器就能满足要求,你就没有必要使用memcached。它的最大好处就是跨服务器,跨应用共享缓存。