原創:實現ehcache動態創建cache,以及超期判斷的具體邏輯


當前最常用的三個緩存組件:ehcache、redis、memcached

其中,ehcache與應用共同運行於JVM中,屬於嵌入式組件,運行效率最高,因此常被用於實現一級緩存。

在更復雜的一些系統中,由於ehcache對集群/分布式的支持相對較弱,因此還會集成redis、memcached等,實現二級緩存。

ehcache的用法非常簡單,只需要引入相關的Jar包,並創建一個配置文件,就可以在開發中使用了。

 

1、在Maven中添加ehcache的依賴:

<dependency>
    <groupId>net.sf.ehcache</groupId>
    <artifactId>ehcache</artifactId>
    <version>2.10.4</version>
</dependency>

這個版本是發布到net.sf.ehcache的最后一個版本,也是2.x的最后一個版本。

org.ehcache上有更新的3.x版本,功能更強大,寫法差異也挺大。

由於2.x的核心功能已經非常穩定,已經完全滿足系統需求,也更熟悉,因此我還是選擇了這個2.10.4的版本。

 

2、創建配置文件:ehcache.xml

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"
         updateCheck="false" 
         dynamicConfig="false">

    <diskStore path="java.io.tmpdir/myApp"/>

    <!-- 
    默認緩存
    
    屬性說明:
        maxElementsInMemory:內存中可保存的最大數量
        eternal:緩存中對象是否為永久的。如果是,超時設置將被忽略
        timeToIdleSeconds:對象最后一次訪問之后的存活時間
        timeToLiveSeconds:對象創建后的存活時間
        memoryStoreEvictionPolicy:內存緩存的超期清理策略
        maxElementsOnDisk:硬盤中可保存的最大數量
        diskExpiryThreadIntervalSeconds:磁盤超期監控線程掃描時間間隔
        overflowToDisk:內存不足時,是否啟用磁盤緩存
    -->
    <defaultCache 
        maxElementsInMemory="10000" 
        eternal="false"
        timeToIdleSeconds="1200" 
        maxElementsOnDisk="10000000"
        diskExpiryThreadIntervalSeconds="120" 
        memoryStoreEvictionPolicy="LRU"
        overflowToDisk="true">
    </defaultCache>
    
</ehcache>

把這個配置文件保存到 src/main/resource 目錄下即可。

 

3、實現動態創建Cache

在ehcache中,有兩個最基本的對象:Cache Element

其中,Cache相當於ehcache的分區,可以看成memcache中的Slab,上面配置文件中的 defaultCache 就是一個默認分區

每個Cache(分區)都類似一個Map<K, V>,可以通過Key來從Cache中返回緩存的Value

每個KV鍵值對,在ehcache中,被稱為Element(元素)。

 

要將任何一個對象添加到ehcache中,都需要事先指定分區

但在配置文件中創建分區很麻煩,通常只創建一個默認分區(必須存在),然后通過一個方法來動態創建分區:

/**
 * 獲取Cache,當Cache不存在時自動創建
 * 
 * @param cacheName
 * @return Cache
* @author netwild@qq.com */ public Cache getOrAddCache(String cacheName) { Cache cache = cacheManager.getCache(cacheName); if (cache == null) { synchronized (locker) { cache = cacheManager.getCache(cacheName); if (cache == null) { cacheManager.addCacheIfAbsent(cacheName); cache = cacheManager.getCache(cacheName); } } } return cache; }

這樣的話,只需要像下面的用法,就可以很方便的把對象添加到緩存中:

String cacheName = "article";
String atricleId = "A00428";
Atricle article = AtricleService.findById(atricleId);
Element element = new Element(atricleId, article); getOrAddCache(cacheName).put(element);

動態創建的Cache並不會出現在ehcache.xml配置文件中。

值得注意的是,上面動態創建Cache的方法中,並沒有為新的Cache指定任何參數,那這些參數的默認值是多少呢?

其實,當創建Cache時,如果未傳入參數默認值,將自動拷貝 defaultCache 的參數設置

就是說,配置文件中 defaultCache 的超期時間等屬性將直接被應用到所有動態創建的Cache。

 

4、ehcache關於元素超期的判斷邏輯

在ehcache.xml配置文件中,有兩個關於元素超期的參數:

timeToLiveSeconds:對象創建后的存活時間

timeToIdleSeconds:對象最后一次訪問之后的存活時間

這兩個參數我忽略了將近一年的時間,一直在配置文件中對他們都設置了同樣的參數值,比如:1200(20分鍾)

剛才反復實驗多次,終於將這兩個參數搞清楚,才明白以前的做法是錯誤的

首先,這兩個參數都可以單獨設置而省略另一個,也可以分別設置成不同的值,當然也可以設置成相同的值,就像我以前做的那樣

下面分別對這幾種進行說明

1)單獨設置 timeToLiveSeconds

該對象的超期時間 = 初始創建時間 + timeToLiveSeconds

因為初始創建時間是固定的,因此不管這個對象在有效期內被命中了多少次,一旦滿足超期條件,該對象將被移除。

2)單獨設置 timeToIdleSeconds

該對象的超時時間 = 最近訪問時間 + timeToIdleSeconds

注意與上面的區別:不再根據創建時間,而是根據最近訪問時間來確定超期時間

所以這是一種動態的超期模式,即使這個參數設置為1(秒),只要保證每秒內都能get一次,那么對象也將永遠不會超期。

3)分別設置 timeToLiveSeconds 及 timeToIdleSeconds

那么將分別計算以上兩種模式的超期時間,會得出兩個結果,再從兩個結果里找到最小的一個做為超期時間,相當於“嚴苛模式

但事實上,創建時間肯定會小於最近訪問時間,那如果兩者都設置同樣的參數值,相當於 timeToIdleSeconds 永遠也不會起到作用。

如果設置不同的參數值,根據具體的業務需求,可能會出現一些意料之中或者意料之外的情況。

 

綜上所述,我的建議是,單獨設置 timeToIdleSeconds 更恰當一些,對於在有效期內被頻繁命中的緩存對象,可以自動“續期”。

 

5、最常用的操作之一:判斷緩存中是否存在對象

 

在應用緩存的開發過程中,這是最常用的操作,目的是想要知道:目標對象是否已經被緩存過

通常下面的邏輯是:如果已被緩存過,那么直接拿出來使用;否則自力更生,完事之后再添加到緩存,下次就省事了

很多人是這樣判斷的:

return getOrAddCache(cacheName).get(key) != null;

但這種方式存在個問題:當緩存對象實際上存在,但值就是Null,這時就相當於忽略了緩存

所以我開始時是這樣判斷的:

return getOrAddCache(cacheName).isKeyInCache(key);

后來發現這種方式不會進行超期驗證,就是說即使對象已經超期,只要當初被創建過,也會返回true

調整之后:

Cache cache = getOrAddCache(cacheName);
if(cache.isKeyInCache(key) && cache.getQuiet(key) != null){
    return true;
}
return false;

這樣就准確了!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM