Ehcache 中ehcache.xml 配置詳解和示例

EhCache 是一個純Java的進程內緩存框架,具有快速、精幹等特點,是Hibernate中默認的CacheProvider。

Ehcache是一種廣泛使用的開源Java分佈式緩存。主要面向通用緩存,Java EE和輕量級容器。它具有內存和磁盤存儲,緩存加載器,緩存擴展,緩存異常處理程序,一個gzip緩存servlet過濾器,支持REST和SOAP api等特點。
Ehcache最初是由Greg Luck於2003年開始開發。2009年,該項目被Terracotta購買。軟件仍然是開源,但一些新的主要功能(例如,快速可重啓性之間的一致性的)只能在商業產品中使用,例如Enterprise EHCache and BigMemory。維基媒體Foundationannounced目前使用的就是Ehcache技術。
主要的特性有:
1. 快速
2. 簡單
3. 多種緩存策略
4. 緩存數據有兩級:內存和磁盤,因此無需擔心容量問題
5. 緩存數據會在虛擬機重啓的過程中寫入磁盤
6. 可以通過RMI、可插入API等方式進行分佈式緩存
7. 具有緩存和緩存管理器的偵聽接口
8. 支持多緩存管理器實例,以及一個實例的多個緩存區域
9. 提供Hibernate的緩存實現

Ehcache 中ehcache.xml 配置詳解和示例

 

Xml代碼  收藏代碼
  1. <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">     
  2.     
  3.     <diskStore path="java.io.tmpdir"/>     
  4.     
  5.     <!--     
  6.     Mandatory Default Cache configuration. These settings will be applied to caches     
  7.     created programmtically using CacheManager.add(String cacheName)     
  8.     -->     
  9.     <!--     
  10.        name:緩存名稱。     
  11.        maxElementsInMemory:緩存最大個數。     
  12.        eternal:對象是否永久有效,一但設置了,timeout將不起作用。     
  13.        timeToIdleSeconds:設置對象在失效前的允許閒置時間(單位:秒)。僅當eternal=false對象不是永久有效時使用,可選屬性,默認值是0,也就是可閒置時間無窮大。     
  14.        timeToLiveSeconds:設置對象在失效前允許存活時間(單位:秒)。最大時間介於創建時間和失效時間之間。僅當eternal=false對象不是永久有效時使用,默認是0.,也就是對象存活時間無窮大。     
  15.        overflowToDisk:當內存中對象數量達到maxElementsInMemory時,Ehcache將會對象寫到磁盤中。     
  16.        diskSpoolBufferSizeMB:這個參數設置DiskStore(磁盤緩存)的緩存區大小。默認是30MB。每個Cache都應該有自己的一個緩衝區。     
  17.        maxElementsOnDisk:硬盤最大緩存個數。     
  18.        diskPersistent:是否緩存虛擬機重啓期數據 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.     
  19.        diskExpiryThreadIntervalSeconds:磁盤失效線程運行時間間隔,默認是120秒。     
  20.        memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理內存。默認策略是LRU(最近最少使用)。你可以設置爲FIFO(先進先出)或是LFU(較少使用)。     
  21.        clearOnFlush:內存數量最大時是否清除。     
  22.     -->     
  23.     <defaultCache     
  24.             maxElementsInMemory="10000"    
  25.             eternal="false"    
  26.             timeToIdleSeconds="120"    
  27.             timeToLiveSeconds="120"    
  28.             overflowToDisk="true"    
  29.             maxElementsOnDisk="10000000"    
  30.             diskPersistent="false"    
  31.             diskExpiryThreadIntervalSeconds="120"    
  32.             memoryStoreEvictionPolicy="LRU"    
  33.             />     
  34. </ehcache>    

 

最後複習一下ehcache 中緩存的3 種清空策略:

1 FIFO ,first in first out ,這個是大家最熟的,先進先出,不多講了

2 LFU , Less Frequently Used ,就是上面例子中使用的策略,直白一點就是講一直以來最少被使用的。如上面所講,緩存的元素有一個hit 屬性,hit 值最小的將會被清出緩存。

2 LRU ,Least Recently Used ,最近最少使用的,緩存的元素有一個時間戳,當緩存容量滿了,而又需要騰出地方來緩存新的元素的時候,那麼現有緩存元素中時間戳離當前時間最遠的元素將被清出緩存。

---------------------------------------------------------------------------------------------------------------------

配置元素說明:

diskStore:配置DiskStore,當需要使用磁盤保存的時候,需要對象實現序列化接口 

    屬性:path 配置文件存儲位置,如user.home,user.dir,java.io.tmpdir 

defaultCache: 默認緩存配置 

    必須屬性: 
        name:設置緩存的名稱,用於標誌緩存,惟一 
        maxElementsInMemory:在內存中最大的對象數量 
        maxElementsOnDisk:在DiskStore中的最大對象數量,如爲0,則沒有限制 
        eternal:設置元素是否永久的,如果爲永久,則timeout忽略 
        overflowToDisk:是否當memory中的數量達到限制後,保存到Disk 

    可選的屬性: 
        timeToIdleSeconds:設置元素過期前的空閒時間 
        timeToLiveSeconds:設置元素過期前的活動時間 
        diskPersistent:是否disk store在虛擬機啓動時持久化。默認爲false 
        diskExpiryThreadIntervalSeconds:運行disk終結線程的時間,默認爲120秒 
        memoryStoreEvictionPolicy:策略關於Eviction 

緩存子元素: 
    
    cacheEventListenerFactory:註冊相應的的緩存監聽類,用於處理緩存事件,如put,remove,update,和expire 
    bootstrapCacheLoaderFactory:指定相應的BootstrapCacheLoader,用於在初始化緩存,以及自動設置。

---------------------------------------------------------------------------------------------------------------------

 

在開發高併發量,高性能的網站應用系統時,緩存Cache起到了非常重要的作用。本文主要介紹EHCache的使用,以及使用EHCache的實踐經驗。
筆者使用過多種基於Java的開源Cache組件,其中包括OSCache、JBossCache、EHCache。OSCache功能強大,使用靈活,可用於對象緩存、Filter緩存以及在JSP中直接使用cache標籤。筆者在最近的使用過程中發現,在併發量較高時,OSCache會出現線程阻塞和數據錯誤,通過分析源代碼發現是其內部實現的缺陷。JBossCache最大的優點是支持基於對象屬性的集羣同步,不過JBossCache的配置使用都較複雜,在併發量較高的情況下,對象屬性數據在集羣中同步也會加大系統的開銷。以上兩種Cache本文僅作簡單介紹,不做深入探討。
EHCache是來自sourceforge(http://ehcache.sourceforge.net/)的開源項目,也是純Java實現的簡單、快速的Cache組件。EHCache支持內存和磁盤的緩存,支持LRU、LFU和FIFO多種淘汰算法,支持分佈式的Cache,可以作爲Hibernate的緩存插件。同時它也能提供基於Filter的Cache,該Filter可以緩存響應的內容並採用Gzip壓縮提高響應速度。

 EHCache API的基本用法
首先介紹CacheManager類。它主要負責讀取配置文件,默認讀取CLASSPATH下的ehcache.xml,根據配置文件創建並管理Cache對象。
// 使用默認配置文件創建CacheManager
CacheManager manager = CacheManager.create();
// 通過manager可以生成指定名稱的Cache對象
Cache cache = cache = manager.getCache("demoCache");
// 使用manager移除指定名稱的Cache對象
manager.removeCache("demoCache");
可以通過調用manager.removalAll()來移除所有的Cache。通過調用manager的shutdown()方法可以關閉CacheManager。
有了Cache對象之後就可以進行一些基本的Cache操作,例如:
//往cache中添加元素
Element element = new Element("key", "value");
cache.put(element);
//從cache中取回元素
Element element = cache.get("key");
element.getValue();
//從Cache中移除一個元素
cache.remove("key");
可以直接使用上面的API進行數據對象的緩存,這裏需要注意的是對於緩存的對象都是必須可序列化的。在下面的篇幅中筆者還會介紹EHCache和Spring、Hibernate的整合使用。

 配置文件
配置文件ehcache.xml中命名爲demoCache的緩存配置:
<cache name="demoCache"
maxElementsInMemory="10000"
eternal="false"
overflowToDisk="true"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU" />

各配置參數的含義:
maxElementsInMemory:緩存中允許創建的最大對象數
eternal:緩存中對象是否爲永久的,如果是,超時設置將被忽略,對象從不過期。
timeToIdleSeconds:緩存數據的鈍化時間,也就是在一個元素消亡之前,兩次訪問時間的最大時間間隔值,這只能在元素不是永久駐留時有效,如果該值是 0 就意味着元素可以停頓無窮長的時間。
timeToLiveSeconds:緩存數據的生存時間,也就是一個元素從構建到消亡的最大時間間隔值,這只能在元素不是永久駐留時有效,如果該值是0就意味着元素可以停頓無窮長的時間。
overflowToDisk:內存不足時,是否啓用磁盤緩存。
memoryStoreEvictionPolicy:緩存滿了之後的淘汰算法。LRU和FIFO算法這裏就不做介紹。LFU算法直接淘汰使用比較少的對象,在內存保留的都是一些經常訪問的對象。對於大部分網站項目,該算法比較適用。
如果應用需要配置多個不同命名並採用不同參數的Cache,可以相應修改配置文件,增加需要的Cache配置即可。

 利用Spring APO整合EHCache
首先,在CLASSPATH下面放置ehcache.xml配置文件。在Spring的配置文件中先添加如下cacheManager配置:
<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
</bean>
配置demoCache:
<bean id="demoCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager" ref="cacheManager" />
<property name="cacheName">
<value>demoCache</value>
</property>
</bean>
接下來,寫一個實現org.aopalliance.intercept.MethodInterceptor接口的攔截器類。有了攔截器就可以有選擇性的配置想要緩存的 bean 方法。如果被調用的方法配置爲可緩存,攔截器將爲該方法生成 cache key 並檢查該方法返回的結果是否已緩存。如果已緩存,就返回緩存的結果,否則再次執行被攔截的方法,並緩存結果供下次調用。具體代碼如下:
public class MethodCacheInterceptor implements MethodInterceptor,
InitializingBean {

private Cache cache;


public void setCache(Cache cache) {
this.cache = cache;
}


public void afterPropertiesSet() throws Exception {
Assert.notNull(cache,
"A cache is required. Use setCache(Cache) to provide one.");
}


public Object invoke(MethodInvocation invocation) throws Throwable {
String targetName = invocation.getThis().getClass().getName();
String methodName = invocation.getMethod().getName();
Object[] arguments = invocation.getArguments();
Object result;

String cacheKey = getCacheKey(targetName, methodName, arguments);
Element element = null;
synchronized (this){
element = cache.get(cacheKey);

if (element == null) {
//調用實際的方法
result = invocation.proceed();
element = new Element(cacheKey, (Serializable) result);
cache.put(element);
}
}
return element.getValue();
}


private String getCacheKey(String targetName, String methodName,
Object[] arguments) {
StringBuffer sb = new StringBuffer();
sb.append(targetName).append(".").append(methodName);
if ((arguments != null) && (arguments.length != 0)) {
for (int i = 0; i < arguments.length; i++) {
sb.append(".").append(arguments[i]);
}
}
return sb.toString();
}
}
synchronized (this)這段代碼實現了同步功能。爲什麼一定要同步?Cache對象本身的get和put操作是同步的。如果我們緩存的數據來自數據庫查詢,在沒有這段同步代碼時,當key不存在或者key對應的對象已經過期時,在多線程併發訪問的情況下,許多線程都會重新執行該方法,由於對數據庫進行重新查詢代價是比較昂貴的,而在瞬間大量的併發查詢,會對數據庫服務器造成非常大的壓力。所以這裏的同步代碼是很重要的。

接下來,繼續完成攔截器和Bean的配置:
<bean id="methodCacheInterceptor" class="com.xiebing.utils.interceptor.MethodCacheInterceptor">
<property name="cache">
<ref local="demoCache" />
</property>
</bean>
<bean id="methodCachePointCut" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice">
<ref local="methodCacheInterceptor" />
</property>
<property name="patterns">
<list>
<value>.*myMethod</value>
</list>
</property>
</bean>

<bean id="myServiceBean"
class="com.xiebing.ehcache.spring.MyServiceBean">
</bean>

<bean id="myService" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target">
<ref local="myServiceBean" />
</property>
<property name="interceptorNames">
<list>
<value>methodCachePointCut</value>
</list>
</property>
</bean>

其中myServiceBean是實現了業務邏輯的Bean,裏面的方法myMethod()的返回結果需要被緩存。這樣每次對myServiceBean的myMethod()方法進行調用,都會首先從緩存中查找,其次纔會查詢數據庫。使用AOP的方式極大地提高了系統的靈活性,通過修改配置文件就可以實現對方法結果的緩存,所有的對Cache的操作都封裝在了攔截器的實現中。

 CachingFilter功能
使用Spring的AOP進行整合,可以靈活的對方法的的返回結果對象進行緩存。CachingFilter功能可以對HTTP響應的內容進行緩存。這種方式緩存數據的粒度比較粗,例如緩存整張頁面。它的優點是使用簡單、效率高,缺點是不夠靈活,可重用程度不高。
EHCache使用SimplePageCachingFilter類實現Filter緩存。該類繼承自CachingFilter,有默認產生cache key的calculateKey()方法,該方法使用HTTP請求的URI和查詢條件來組成key。也可以自己實現一個Filter,同樣繼承CachingFilter類,然後覆寫calculateKey()方法,生成自定義的key。
在筆者參與的項目中很多頁面都使用AJAX,爲保證JS請求的數據不被瀏覽器緩存,每次請求都會帶有一個隨機數參數i。如果使用SimplePageCachingFilter,那麼每次生成的key都不一樣,緩存就沒有意義了。這種情況下,我們就會覆寫calculateKey()方法。

要使用SimplePageCachingFilter,首先在配置文件ehcache.xml中,增加下面的配置:
<cache name="SimplePageCachingFilter" maxElementsInMemory="10000" eternal="false"
overflowToDisk="false" timeToIdleSeconds="300" timeToLiveSeconds="600"
memoryStoreEvictionPolicy="LFU" />
其中name屬性必須爲SimplePageCachingFilter,修改web.xml文件,增加一個Filter的配置:
<filter>
<filter-name>SimplePageCachingFilter</filter-name>
<filter-class>net.sf.ehcache.constructs.web.filter.SimplePageCachingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>SimplePageCachingFilter</filter-name>
<url-pattern>/test.jsp</url-pattern>
</filter-mapping>

下面我們寫一個簡單的test.jsp文件進行測試,緩存後的頁面每次刷新,在600秒內顯示的時間都不會發生變化的。代碼如下:
<%
out.println(new Date());
%>

CachingFilter輸出的數據會根據瀏覽器發送的Accept-Encoding頭信息進行Gzip壓縮。經過筆者測試,Gzip壓縮後的數據量是原來的1/4,速度是原來的4-5倍,所以緩存加上壓縮,效果非常明顯。
在使用Gzip壓縮時,需注意兩個問題:
1. Filter在進行Gzip壓縮時,採用系統默認編碼,對於使用GBK編碼的中文網頁來說,需要將操作系統的語言設置爲:zh_CN.GBK,否則會出現亂碼的問題。
2. 默認情況下CachingFilter會根據瀏覽器發送的請求頭部所包含的Accept-Encoding參數值來判斷是否進行Gzip壓縮。雖然IE6/7瀏覽器是支持Gzip壓縮的,但是在發送請求的時候卻不帶該參數。爲了對IE6/7也能進行Gzip壓縮,可以通過繼承CachingFilter,實現自己的Filter,然後在具體的實現中覆寫方法acceptsGzipEncoding。

具體實現參考:
protected boolean acceptsGzipEncoding(HttpServletRequest request) {
final boolean ie6 = headerContains(request, "User-Agent", "MSIE 6.0");
final boolean ie7 = headerContains(request, "User-Agent", "MSIE 7.0");
return acceptsEncoding(request, "gzip") || ie6 || ie7;
}

 EHCache在Hibernate中的使用
EHCache可以作爲Hibernate的二級緩存使用。在hibernate.cfg.xml中需增加如下設置:
<prop key="hibernate.cache.provider_class">
org.hibernate.cache.EhCacheProvider
</prop>
然後在Hibernate映射文件的每個需要Cache的Domain中,加入類似如下格式信息:
<cache usage="read-write|nonstrict-read-write|read-only" />
比如:
<cache usage="read-write" />
最後在配置文件ehcache.xml中增加一段cache的配置,其中name爲該domain的類名。
<cache name="domain.class.name"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
overflowToDisk="false"
/>

 EHCache的監控
對於Cache的使用,除了功能,在實際的系統運營過程中,我們會比較關注每個Cache對象佔用的內存大小和Cache的命中率。有了這些數據,我們就可以對Cache的配置參數和系統的配置參數進行優化,使系統的性能達到最優。EHCache提供了方便的API供我們調用以獲取監控數據,其中主要的方法有:

//得到緩存中的對象數
cache.getSize();
//得到緩存對象佔用內存的大小
cache.getMemoryStoreSize();
//得到緩存讀取的命中次數
cache.getStatistics().getCacheHits()
//得到緩存讀取的錯失次數
cache.getStatistics().getCacheMisses()

 分佈式緩存
EHCache從1.2版本開始支持分佈式緩存。分佈式緩存主要解決集羣環境中不同的服務器間的數據的同步問題。具體的配置如下:

在配置文件ehcache.xml中加入
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=automatic, multicastGroupAddress=230.0.0.1, multicastGroupPort=4446"/>

<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"/>

另外,需要在每個cache屬性中加入
<cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>
例如:
<cache name="demoCache"
maxElementsInMemory="10000"
eternal="true"
overflowToDisk="true">
<cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>
</cache>

總結
EHCache是一個非常優秀的基於Java的Cache實現。它簡單、易用,而且功能齊全,並且非常容易與Spring、Hibernate等流行的開源框架進行整合。通過使用EHCache可以減少網站項目中數據庫服務器的訪問壓力,提高網站的訪問速度,改善用戶的體驗。






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章