ehcache缓冲技术

本文详细介绍了如何使用ehcache在网站中实现页面缓存,包括缓存策略、配置方法及性能优化,旨在提高服务器压力和客户端响应速度。

关于缓存的话题,在坛子里已经有很多讨论,简单的来说,如果一个应用中80% 的时间内都在访问20% 的数据,那么,这时候就应该使用缓存了。这个和长尾理论正好相悖,其实也不是相悖,只是不同的理论使用的场景不同。在80/20 原则生效的地方,我们都应该考虑是否可以使用缓存。但即使是这样,缓存也有不同的用法,举个例子,一个网站的首页估计是被访问的次数最多的,我们可以考虑给首页做一个页面缓存,而如果在某个页面上,比如说javaeye 的java 版区只有前几个页面是访问最频繁的,(假设javaeye 是使用hibernate ,当然这只是假设,我们都知道javaeye 是使用ror 开发的)那么我们就可以考虑给java 版区的record 做二级缓存了,因为二级缓存中是按照对象的id 来保存的,所以应该来说这前面几页使用的对象会一直存在于缓存之中(如何使用hibernate 的二级缓存坛子上也有介绍)。由此可见不同的页面的缓存策略有可能有天壤之别。

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

 

那么我们来看一下如何使用ehcache 做到这一点。

 

在使用ehcache 的页面缓存之前,我们必须要了解ehcache 的几个概念,

1 timeToIdleSeconds ,多长时间不访问该缓存,那么ehcache 就会清除该缓存。

2 timeToLiveSeconds ,缓存的存活时间,从开始创建的时间算起。

 

看到这里,我们知道,首页的页面缓存的存活时间,我们定的是2 分钟,那么也就是说我们的timeToLiveSeconds 应该设置为120 ,同时我们的timeToIdleSeconds 最好也设置为2 分钟,或者小于2 分钟。我们来看一下下面这个配置,这个配置片段应该放到ehcache.xml 中:

 

< cache name = "SimplePageCachingFilter"

           maxElementsInMemory = "10"

           maxElementsOnDisk = "10"

           eternal = "false"

            overflowToDisk = "true"

           diskSpoolBufferSizeMB = "20"

           timeToIdleSeconds = "10"

           timeToLiveSeconds = "10"

           memoryStoreEvictionPolicy = "LFU"

            />

 

SimplePageCachingFilter 是缓存的名字,maxElementsInMemory 表示内存中SimplePageCachingFilter 缓存中元素的最大数量为10 ,maxElementsOnDisk 是指持久化该缓存的元素到硬盘上的最大数量也为10 (),eternal=false 意味着该缓存会死亡。overflowToDisk=true 意思是表示当缓存中元素的数量超过限制时,就把这些元素持久化到硬盘,如果overflowToDisk 是false ,那么maxElementsOnDisk 的设置就没有什么意义了。memoryStoreEvictionPolicy=LFU 是指按照缓存的hit 值来清除,也就是说缓存满了之后,新的对象需要缓存时,将会将缓存中hit 值最小的对象清除出缓存,给新的对象腾出地方来了(文章最后有ehcache 中自带的3 种缓存清空策略的介绍)。

 

接着我们来看一下SimplePageCachingFilter 的配置,

< filter >

        < filter-name > indexCacheFilter filter-name >

        < filter-class >

            net.sf.ehcache.constructs.web.filter.SimplePageCachingFilter

        filter-class >

filter >

 

    < filter-mapping >

        < filter-name > indexCacheFilter filter-name >

        < url-pattern > *index.action url-pattern >

filter-mapping >

就只需要这么多步骤,我们就可以给某个页面做一个缓存的,把上面这段配置放到你的web.xml 中,那么当你打开首页的时候,你会发现,2 分钟才会有一堆sql 语句出现在控制台上。当然你也可以调成5 分钟,总之一切都在控制中。

 

好了,缓存整个页面看上去是非常的简单,甚至都不需要写一行代码,只需要几行配置就行了,够简单吧,虽然看上去简单,但是事实上内部实现却不简单哦,有兴趣的话,大家可以看看SimplePageCachingFilter 继承体系的源代码。

 

上面的配置针对的情况是缓存首页的全部,如果你只想缓存首页的部分内容时,你需要使用SimplePageFragmentCachingFilter 这个filter 。我们看一下如下片断:

 

< filter >

        < filter-name > indexCacheFilter filter-name >

        < filter-class >

            net.sf.ehcache.constructs.web.filter.SimplePageFragmentCachingFilter

        filter-class >

filter >

 

    < filter-mapping >

        < filter-name > indexCacheFilter filter-name >

        < url-pattern > */index_right.jsp url-pattern >

filter-mapping >

 

这个jsp 需要被jsp:include 到其他页面,这样就做到的局部页面的缓存。这一点貌似没有oscache 的tag 好用。

 

事实上在cachefilter 中还有一个特性,就是gzip ,也就是说缓存中的元素是被压缩过的,如果客户浏览器支持压缩的话,filter 会直接返回压缩过的流,这样节省了带宽,把解压的工作交给了客户浏览器,如果客户的浏览器不支持gzip ,那么filter 会把缓存的元素拿出来解压后再返回给客户浏览器(大多数爬虫是不支持gzip 的,所以filter 也会解压后再返回流),这样做的优点是节省带宽,缺点就是增加了客户浏览器的负担(但是我觉得对当代的计算机而言,这个负担微乎其微)。

 

好了,如果你的页面正好也需要用到页面缓存,不防可以考虑一下ehcache ,因为它实在是非常简单,而且易用。

 

总结:ehcache 是一个非常轻量级的缓存实现,而且从1.2 之后就支持了集群,目前的最新版本是1.3 ,而且是hibernate 默认的缓存provider 。虽然本文是介绍的是ehcache 对页面缓存的支持,但是ehcache 的功能远不止如此,当然要使用好缓存,对JEE 中缓存的原理,使用范围,适用场景等等都需要有比较深刻的理解,这样才能用好缓存,用对缓存。

 

最后复习一下ehcache 中缓存的3 种清空策略:

1 FIFO ,first in first out ,这个是大家最熟的,先进先出,不多讲了

2 LFU , Less Frequently Used ,就是上面例子中使用的策略,直白一点就是讲一直以来最少被使用的。如上面所讲,缓存的元素有一个hit 属性,hit 值最小的将会被清出缓存。

2 LRU ,Least Recently Used ,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。

<?xml version="1.0" encoding="UTF-8"?>
<ehcache>
<!--timeToIdleSeconds 当缓存闲置n秒后销毁 -->
<!--timeToLiveSeconds 当缓存存活n秒后销毁 -->
<!--
缓存配置
       name:缓存名称。
       maxElementsInMemory:缓存最大个数。
       eternal:对象是否永久有效,一但设置了,timeout将不起作用。
       timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
       timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
       overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
       diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
       maxElementsOnDisk:硬盘最大缓存个数。
       diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
       diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
       memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
       clearOnFlush:内存数量最大时是否清除。
-->
<diskStore path="java.io.tmpdir" />
<defaultCache
  maxElementsInMemory="500"
  eternal="false"
  timeToIdleSeconds="300"
  timeToLiveSeconds="1200"
  overflowToDisk="true" />
    <cache name="com.Menu" maxElementsInMemory="150" eternal="false" timeToLiveSeconds="36000" timeToIdleSeconds="3600" overflowToDisk="true"/>
</ehcache>

 

iBATIS中使用EHCache

需要实现CacheController接口并通过一系列的配置委托iBATIS使用EHCache进行缓存.

  1. package com.ibatis.sqlmap.engine.cache.EhCacheController;
  2. import java.net.URL;
  3. import java.util.Properties;
  4. import net.sf.ehcache.Cache;
  5. import net.sf.ehcache.CacheManager;
  6. import net.sf.ehcache.Element;
  7. import com.ibatis.sqlmap.engine.cache.CacheController;
  8. import com.ibatis.sqlmap.engine.cache.CacheModel;
  9. /**
  10. * EhCache Implementation of the {@link com.ibatis.sqlmap.engine.cache.CacheController} interface to be able to use
  11. * EhCache as a cache implementation in iBatis. You can configure your cache model as follows, by example, in your
  12. * sqlMapping files:
  13. * &lt;cacheModel id="myCache" type="nl.rabobank.springproject.ibatis.EhCacheController" readOnly="true" serialize="false"&gt;
  14. *    &lt;property name="configFile" value="/path-to-ehcache.xml"/&gt;
  15. * &lt;/cacheModel&gt;
  16. * Alternatively, you can use a type alias in your type attribute and defining the class with a
  17. * &lt;TypeAlias&gt; declaration, see iBatis documentation on how to do this.
  18. */
  19. public class EhCacheController implements CacheController {
  20.     /** The EhCache CacheManager. */
  21.     private CacheManager cacheManager;
  22.     /**
  23.       * Flush a cache model.
  24.       * @param cacheModel - the model to flush.
  25.       */
  26.     public void flush(CacheModel cacheModel) {
  27.          getCache(cacheModel).removeAll();
  28.      }
  29.     /**
  30.       * Get an object from a cache model.
  31.       * @param cacheModel - the model.
  32.       * @param key         - the key to the object.
  33.       * @return the object if in the cache, or null(?).
  34.       */
  35.     public Object getObject(CacheModel cacheModel, Object key) {
  36.          Object result = null;
  37.          Element element = getCache(cacheModel).get(key);
  38.         if (element != null) {
  39.              result = element.getObjectValue();
  40.          }
  41.         return result;
  42.      }
  43.     /**
  44.       * Put an object into a cache model.
  45.       * @param cacheModel - the model to add the object to.
  46.       * @param key         - the key to the object.
  47.       * @param object      - the object to add.
  48.       */
  49.     public void putObject(CacheModel cacheModel, Object key, Object object) {
  50.          getCache(cacheModel).put(new Element(key, object));
  51.      }
  52.     /**
  53.       * Remove an object from a cache model.
  54.       * @param cacheModel - the model to remove the object from.
  55.       * @param key         - the key to the object.
  56.       * @return the removed object(?).
  57.       */
  58.     public Object removeObject(CacheModel cacheModel, Object key) {
  59.          Object result = this.getObject(cacheModel, key);
  60.          getCache(cacheModel).remove(key);
  61.         return result;
  62.      }
  63.     /**
  64.       * Configure a cache controller. Initialize the EH Cache Manager as a singleton.
  65.       * @param props - the properties object continaing configuration information.
  66.       */
  67.     public void setProperties(Properties props) {
  68.          URL url = getClass().getResource(props.getProperty("configFile"));
  69.          cacheManager = CacheManager.create(url);
  70.      }
  71.     /**
  72.       * Gets an EH Cache based on an iBatis cache Model.
  73.       * @param cacheModel - the cache model.
  74.       * @return the EH Cache.
  75.       */
  76.     private Cache getCache(CacheModel cacheModel) {
  77.          String cacheName = cacheModel.getId();
  78.          Cache cache = cacheManager.getCache(cacheName);
  79.         return cache;
  80.      }
  81.     /**
  82.       * Shut down the EH Cache CacheManager.
  83.       */
  84.     public void finalize() {
  85.         if (cacheManager != null) {
  86.              cacheManager.shutdown();
  87.          }
  88.      }
  89. }
package com.ibatis.sqlmap.engine.cache.EhCacheController;import java.net.URL;import java.util.Properties;import net.sf.ehcache.Cache;import net.sf.ehcache.CacheManager;import net.sf.ehcache.Element;import com.ibatis.sqlmap.engine.cache.CacheController;import com.ibatis.sqlmap.engine.cache.CacheModel;/** * EhCache Implementation of the {@link com.ibatis.sqlmap.engine.cache.CacheController} interface to be able to use * EhCache as a cache implementation in iBatis. You can configure your cache model as follows, by example, in your * sqlMapping files: * &lt;cacheModel id="myCache" type="nl.rabobank.springproject.ibatis.EhCacheController" readOnly="true" serialize="false"&gt; *   &lt;property name="configFile" value="/path-to-ehcache.xml"/&gt; * &lt;/cacheModel&gt; * Alternatively, you can use a type alias in your type attribute and defining the class with a * &lt;TypeAlias&gt; declaration, see iBatis documentation on how to do this. */public class EhCacheController implements CacheController {    /** The EhCache CacheManager. */    private CacheManager cacheManager;    /**     * Flush a cache model.     * @param cacheModel - the model to flush.     */    public void flush(CacheModel cacheModel) {        getCache(cacheModel).removeAll();    }    /**     * Get an object from a cache model.     * @param cacheModel - the model.     * @param key        - the key to the object.     * @return the object if in the cache, or null(?).     */    public Object getObject(CacheModel cacheModel, Object key) {        Object result = null;        Element element = getCache(cacheModel).get(key);        if (element != null) {            result = element.getObjectValue();        }        return result;    }    /**     * Put an object into a cache model.     * @param cacheModel - the model to add the object to.     * @param key        - the key to the object.     * @param object     - the object to add.     */    public void putObject(CacheModel cacheModel, Object key, Object object) {        getCache(cacheModel).put(new Element(key, object));    }    /**     * Remove an object from a cache model.     * @param cacheModel - the model to remove the object from.     * @param key        - the key to the object.     * @return the removed object(?).     */    public Object removeObject(CacheModel cacheModel, Object key) {        Object result = this.getObject(cacheModel, key);        getCache(cacheModel).remove(key);        return result;    }    /**     * Configure a cache controller. Initialize the EH Cache Manager as a singleton.     * @param props - the properties object continaing configuration information.     */    public void setProperties(Properties props) {        URL url = getClass().getResource(props.getProperty("configFile"));        cacheManager = CacheManager.create(url);    }    /**     * Gets an EH Cache based on an iBatis cache Model.     * @param cacheModel - the cache model.     * @return the EH Cache.     */    private Cache getCache(CacheModel cacheModel) {        String cacheName = cacheModel.getId();        Cache cache = cacheManager.getCache(cacheName);        return cache;    }    /**     * Shut down the EH Cache CacheManager.     */    public void finalize() {        if (cacheManager != null) {            cacheManager.shutdown();        }    }}

接下来一个sql Mapping config的例子

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE sqlMap
  3.      PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
  4.      "http://ibatis.apache.org/dtd/sql-map-2.dtd">
  5. <sqlMap namespace="myNamespace">
  6.   <!-- Use type aliases to avoid typing the full classname every time. -->
  7.   <typeAlias alias="MyPojo" type="nl.myproject.MyPojo"/>
  8.   <typeAlias alias="MapCacheController" type="com.ibatis.sqlmap.engine.cache.EhCacheController"/>
  9.   <cacheModel id="MyPojoCache" type="MapCacheController" readOnly="true" serialize="false">
  10.     <property name="configFile" value="/ehcache.xml"/>
  11.   </cacheModel>
  12.   <resultMap id="ResultQueryMap" class="MyPojo">
  13.      ...property mappings go here...
  14.   </resultMap>
  15.   <select id="getMyPojoList" resultMap="ResultQueryMap" cacheModel="MyPojoCache">
  16.      ...select query to get your pojo from the database goes here...
  17.   </select>
  18. </sqlMap>
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE sqlMap    PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"    "http://ibatis.apache.org/dtd/sql-map-2.dtd"><sqlMap namespace="myNamespace">  <!-- Use type aliases to avoid typing the full classname every time. -->  <typeAlias alias="MyPojo" type="nl.myproject.MyPojo"/>  <typeAlias alias="MapCacheController" type="com.ibatis.sqlmap.engine.cache.EhCacheController"/>  <cacheModel id="MyPojoCache" type="MapCacheController" readOnly="true" serialize="false">    <property name="configFile" value="/ehcache.xml"/>  </cacheModel>  <resultMap id="ResultQueryMap" class="MyPojo">    ...property mappings go here...  </resultMap>  <select id="getMyPojoList" resultMap="ResultQueryMap" cacheModel="MyPojoCache">    ...select query to get your pojo from the database goes here...  </select></sqlMap>

一个eh-cache.xml配置示例

  1. <ehcache>
  2.    ...put your default cache here...
  3.   <cache
  4.     name="myNamespace.MyPojoCache"
  5.     maxElementsInMemory="5"
  6.     eternal="false"
  7.     timeToLiveSeconds="60"
  8.     overflowToDisk="false"
  9.     memoryStoreEvictionPolicy="LRU"/>
  10. </ehcache>
<ehcache>  ...put your default cache here...  <cache    name="myNamespace.MyPojoCache"    maxElementsInMemory="5"    eternal="false"    timeToLiveSeconds="60"    overflowToDisk="false"    memoryStoreEvictionPolicy="LRU"/></ehcache>

需要留意的是:

1.<cacheMode>节点配置在sqlMap.xml中,需要放在classpath下.

2.你可以在sqlMap.xml中添加多个<cacheModel>同时指向一个<MapCacheController>.如果CacheManager是单例模式,那么这将减少很多不必要的配置,但是这只是一个初级的问题.

3.必须设置sqlMapConfig.xml中<settings>节点中的cacheModelsEnabled=true,否则缓存不会被启用.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值