你的位置:首页 > Java教程

[Java教程]ehcache实现页面整体缓存和页面局部缓存


之前写过spring cache和ehcache的基本介绍和注解实现缓存管理,今天记录下web项目的页面缓存技术。

 

页面缓存是否有必要?

     这样说吧,几乎所有的网站的首页都是访问率最高的,而首页上的数据来源又是非常广泛的,大多数来自不同的对象,而且有可能来自不同的db ,所以给首页做缓存是很必要的。那么主页的缓存策略应该怎样设计呢?我认为应该是某个固定时间之内不变的,比如说2分钟更新一次。那么这个缓存应该做在什么地方呢?让我们来看一下,当前我们的的应用的结构一般是是page-filter-action-service-dao-db ,这个过程中的- 的地方都是可以做缓存的地方,根据页面缓存的特征,应该把页面缓存做到尽量靠近客户的地方,就是在page 和filter 之间,这样的优点就是第一个用户请求之后,页面被缓存,第二个用户再来请求的时候,走到filter 这个请求就结束了,无需再走后面的action-service-dao-db 。带来的好处是服务器压力的减低和客户段页面响应速度的加快。了解了这些之后我们开始介绍重点。

ehcache页面缓存的特点:缓存中的元素是被压缩过的,如果客户浏览器支持压缩的话,filter 会直接返回压缩过的流,这样节省了带宽,把解压的工作交给了客户浏览器,如果客户的浏览器不支持gzip ,那么filter 会把缓存的元素拿出来解压后再返回给客户浏览器(大多数爬虫是不支持gzip 的,所以filter 也会解压后再返回流),这样做的优点是节省带宽,缺点就是增加了客户浏览器的负担

1 .SimplePageCachingFilter

    它ehcache-web模块下页面缓存Filter的一个简单实现,适用于可以压缩的Http响应(response),如HTML、页面缓存使用的key是通过SimplePageCachingFilter的calculateKey()方法获取的。其内部逻辑是获取请求时的URI及后面的查询字符串作为key进行返回,如“/user/index.jsp?name=abc”,这使得它的应用范围非常广。它不依赖于主机名和端口号,这将使得它同样适用于有多个域或多个端口请求同样内容的情况。如果有需要,我们可以对calculateKey方法进行重写,从而实现我们自己的计算key的逻辑。这个是很有必要的,因为在项目中很多页面都使用AJAX,为保证JS请求的数据不被浏览器缓存,每次请求可能都会是不同的数参数。如果使用 SimplePageCachingFilter,那么每次生成的key都不一样,缓存就没有意义了。这种情况下,我们就会覆写 calculateKey()方法。

2 .SimpleCachingHeadersPageCachingFilter 提供HTTP缓存头信息,这个不介绍了用的很少。

3 .SimplePageFragmentCachingFilter

    SimplePageCachingFilter适用于缓存整个页面的情况,如果只需要缓存某一个片段,如使用jsp:include包含的部分,这个时候就需要用SimplePageFragmentCachingFilter。

第一部分是页面整体缓存

第一步:首先配置ehcache.timeToIdleSeconds的时间爱你是120秒,这里要注意下不要设置太长时间

  <!-- 页面全部缓存 -->  <cache name="SimplePageCachingFilter"      maxElementsInMemory="10"        maxElementsOnDisk="10"         eternal="false"      overflowToDisk="false"      timeToIdleSeconds="120"      timeToLiveSeconds="60"      memoryStoreEvictionPolicy="LFU">
</cache>

第二步 : 在web.

             注意: 如果我们在ehcache.

还有一点url-pattern的指定应该是/pageCacheController/testPageCache.do,而不是/testPageCache.do这个。

  <!--ehcache 页面缓存过滤器 -->  <filter>    <filter-name>PageCachingFilter</filter-name>    <filter-class>net.sf.ehcache.constructs.web.filter.SimplePageCachingFilter</filter-class>    <init-param>       <param-name>cacheName</param-name>       <param-value>SimplePageCachingFilter</param-value>     </init-param>   </filter>    <filter-mapping>    <filter-name>PageCachingFilter</filter-name>    <url-pattern>/pageCacheController/testPageCache.do</url-pattern>  </filter-mapping>

第三步 : 编写controller测试类

@Controller@RequestMapping("pageCacheController")public class PageCacheController {  private final static Logger log = Logger.getLogger(PageCacheController.class);    @RequestMapping("testPageCache")  public ModelAndView testPageCache(){    ModelMap model = new ModelMap();    Date date = new Date();    model.addAttribute("date", date.toLocaleString() );    log.info("我来访问controller了");    return new ModelAndView("testPageCache",model);  }}

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" %><%String path = request.getContextPath();String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head>  <title>测试</title>  <script type="text/javascript" src="<%=request.getContextPath()%>/js/jquery-1.11.1.min.js"></script>    <script type="text/javascript">  </script> </head>  <body> <h1>这是一个页面缓存的练习</h1> <font >${date}</font><br><br> </body></html>

View Code

上面这个是testPageCache.jsp页面

第四步 : 访问/pageCacheController/testPageCache.do观察页面时间并查看控制台输出,在该缓存的生命周期内,即60秒之间刷新页面,页面上的时间是不会变化的,当过了缓存的生命周期在访问,时间立马改变。下面看看图。

在60秒内刷新页面观看控制台。发现“我来访问controller了”没有打印出来,也就是说在缓存生命周期内我们第二次访问时,只经过了page-filter-action-service-dao-db 中的page-filter其余的部分都没有访问。如果你亲自做过的话你会对控制台打印的信息会产生兴趣的,我这里给指出一个重要的Cache-control-> max-age=0,这个你们可以研究研究,我就不说了。

到这里页面整体缓存的简单例子就完成了,当然实际项目中肯定比这复杂,但是道理是一样的。只要你明白其中的原理,那么一切OK!

第二部分是页面局部缓存

 道理跟上面的一样我就简单说了

配置ehcache.

 <!-- 页面局部缓存 -->  <cache name="SimplePageFragmentCachingFilter"      maxElementsInMemory="10"        maxElementsOnDisk="10"         eternal="false"      overflowToDisk="false"      timeToIdleSeconds="120"      timeToLiveSeconds="60"      memoryStoreEvictionPolicy="LFU">  </cache>

 

配置web.<dispatcher>INCLUDE</dispatcher>,如果没有指定任何< dispatcher >元素,默认值是REQUEST就不会拦截了。我这里有新增加了一个页面作为include的页面

 <!--ehcache 页面局部缓存 -->  <filter>    <filter-name>PageFragmentCachingFilter</filter-name>    <filter-class>net.sf.ehcache.constructs.web.filter.SimplePageFragmentCachingFilter</filter-class>    <init-param>       <param-name>cacheName</param-name>       <param-value>SimplePageFragmentCachingFilter</param-value>     </init-param>   </filter>    <!-- This is a filter chain. They are executed in the order below.Do not change the order. -->  <filter-mapping>    <filter-name>PageFragmentCachingFilter</filter-name>    <url-pattern>/page/testPageFragmentCache.jsp</url-pattern>     <dispatcher>INCLUDE</dispatcher>  </filter-mapping>

testPageFragmentCache.jsp页面,为了方便我还是调用了上边的controller方法来观察时间。如果你是按照我的做法一步一步做的话,要测试局部缓存时,需要把页面整体缓存的filter注释掉,实际中不需要,只是我为了偷懒用了相同访问地址
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8" %><%String path = request.getContextPath();String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html> <head>  <title>测试</title> </head>  <body> <h1>这是include的jsp页面</h1> <font >${date}</font><br><br> </body></html>

下面来看效果图

刷新页面,没有变化说明我们include的页面被缓存了

来看看控制台变化

OK页面缓存简单的内容基本写完了,有不合理的地方请大家指正。