Ehcache 基本使用 以及 基於RMI集羣搭建

   ehcache是一個很成熟的基於jvm內存的緩存框架,其效率是要比redis基於socket要高的,一般ehcache可以配合數據庫做一個緩存中間件來減少數據庫的訪問壓力。
   見過很多人包括我自己之前也有這樣的疑問:ehcache比jvm的static 集合好在哪裏。
   1、首先在存儲大小方面 ehcache是和static map存儲差不多的。稍微小一丟丟,算是個可忽略的優勢
   2、ehcache 有自己的緩存管理策略,比如說緩存刷新時機,緩存大小,緩存過期策略等,這是比之static map顯而易見的優點,使用static map 是不好控制緩存大小的,而ehcache 可以很輕鬆的控制 不用我們自己關心
   3、多項目緩存共享問題,其實redis在這裏纔是最好用的,但是個人覺得現在ehcache也有比較不錯的集羣管理了,而static map 也是無法做到的

進入正題

最最開始 添加依賴

	<dependency>
		<groupId>net.sf.ehcache</groupId>
		<artifactId>ehcache-core</artifactId>
		<version>2.6.11</version>
	</dependency>

首先說一下ehcache的基本使用
ehcache 存儲的內容是一個Element ,這東西有個key 和value都是object類型的 其實和map差不多
首先想用它 分三層
第一層:CacheManager 這東西是通過讀配置文件ehcache.xml讀出來的
package是net.sf.ehcache 這裏我使用的是2.6.x版本

CacheManager cacheManager = CacheManager.create(EhcacheManager.class.getClassLoader().getResource("ehcache.xml"));

第二層 Cache 說cache之前 先看一下配置文件了 上面提到的ehcache.xml是個什麼呢

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <diskStore path="java.io.tmpdir/Tmp_EhCache" />
    <defaultCache eternal="false"
                  maxElementsInMemory="1000"
                  overflowToDisk="false"
                  diskPersistent="false"
                  timeToIdleSeconds="0"
                  timeToLiveSeconds="600"
                  memoryStoreEvictionPolicy="LRU" />

    <!-- server1 的cacheManagerPeerProviderFactory配置 -->
    <cache name="articleCache"
           eternal="true"
           maxElementsInMemory="10000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="0"
           timeToLiveSeconds="0"
           memoryStoreEvictionPolicy="LRU" >
    </cache>
</ehcache>

解釋下每個元素的意思 網上太多了

diskStore

diskStore元素:制定一個路徑,當EHCache把數據寫到硬盤上的時候,
會把數據寫到該目錄下。user.home - 用戶主目錄;user.dir - 用戶當前工作目錄;
java.io.tmpdir - 默認臨時文件路徑。

defaultCache

設定緩存的默認數據過期策略。

cache

設定具體的命名緩存的數據過期策略。

name

緩存名稱。通常爲緩存對象的類名;

maxElementsInMemory

設置基於內存的緩存可存放對象的最大數目;

maxElementOnDisk

設置基於硬盤的緩存可存放對象的最大數目;

eternal

如果爲true,表示對象永遠不會過期,此時會忽略tiemToldleSeconds和timeToLiveSeconds屬性
默認爲false。

timeToldleSeconds

設置允許對象處於空閒狀態的最長時間,以秒爲單位。
當對象最近一次被訪問後,如果處於空閒狀態的時間超過了
timeToldleSeconds屬性值,這個對象就會過期。
當對象過期,EHCache將把它從緩存中清空。只有當eternal屬性爲false.
該屬性纔有效。如果該屬性的值爲0,那麼就表示該對象可以無限期地存於緩存中。
即緩存被創建後,最後一次訪問時間到緩存失效之時,兩者之間的間隔,單位爲秒(s)

timeToLiveSeconds

必須大於timeToldleSeconds屬性,纔有意義;
當對象自從被存放到緩存中後,如果處於緩存中的時間超過了 timeToLiveSeconds屬性值,
這個對象就會過期,EHCache將把它從緩存中清除;
即緩存自創建日期起能夠存活的最長時間,單位爲秒(s)

overflowToDisk

如果爲true,表示當基於內存的緩存中的對象數目達到了maxElementsInMemory界限後
會把溢出的對象寫到基於硬盤的緩存中。
注意,如果緩存的對象要寫入到硬盤中的話,則該對象必須實現了Serializable接口才行(也就是序列化);

memoryStoreEvictionPolicy

緩存對象清除策略。
有三種:

   FIFO:first in first out
   先進先出。

   LFU:Less Frequently Used
   一直以來最少被使用策略,緩存元素有一個hit屬性,hit(命中)值最小的將會被清除出緩存。

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

diskSpoolBufferSizeMB

寫入磁盤的緩衝區大小。
由於diskSpoolBufferSizeMB在內部實際是以字節爲單位,
所以最大值是Integer的最大值即2047.99…M,反正不到2G。
所以如果配置的超過2G,將會導致diskSpoolBufferSizeMB爲負數,
在put時ehcache誤以爲磁盤緩存隊列已滿,每次都執行都會阻塞。

maxElementsOnDisk

在DiskStore(磁盤存儲)中的最大對象數量,如爲0,則沒有限制

diskPersistent

是否disk store在虛擬機啓動時持久化。默認爲false

diskExpiryThreadIntervalSeconds

Ehcache後臺線程專門做Ellment失效監測以及清除工作。
此值不宜設置過低,否則會導致清理線程佔用大量CPU資源。
默認值是120秒。

clearOnFlush

當調用flush()是否清除緩存,默認是。

maxEntriesLocalHeap

堆內存中最大緩存對象數,0沒有限制

然後java代碼

Cache articleCache = cacheManager.getCache("articleCache")

這樣就拿到了cache對象 就可以操作Element了
第三層 就是我們開始說的Element
主要就是cache中的一些方法 我只說一些比較常用的

//存儲一個Element  這樣就存儲了一個key爲1  value爲2的Element
articleCache.put(new Element(1, 2));
//有存就有取  看一下 這裏是通過Element 中的key 拿到Element自己
 Element element = articleCache.get(1);
 //通過element拿到value值  這裏可以直接強制類型轉換 轉換成你要的 注意不要轉錯了就行
 Object objectValue = element.getObjectValue();
 Integer i = (Integer)element.getObjectValue();
//拿到所有的key
List keys = articleCache.getKeys();
//通過key集合拿對象   這裏返回的map 是不是很奇怪,其實方便了你的使用map的key 就是 Element中的key  直接get就可以拿出來了
Map<Object, Element> all = articleCache.getAll(keys);

下面我說一下注意事項:
基於Java 傳址不傳值 的特性 從ehcache中取出來的對象 如果直接更改它的屬性 ehcache中的對象會一樣被改。因爲取出來和裏面的是同一個對象。包括放進List 再怎麼怎麼樣 。除非自己再拷貝一個。注意淺拷貝和深拷貝 這裏就不多說了

Springboot 有和ehcache 集成一套註解使用 個人覺得 本來也沒那麼麻煩。那個還要寫表達式,使用也沒這麼靈活。所以我自己不喜歡用。

ehcache 集羣的知識

某度一搜 差不多都是基於官網的翻譯。那張官方圖片已經不知道在幾個地方見過了。
ehcache 集羣有好幾種實現方式,詳情baidu 我這裏直說基於RMI的,所以先說RMI是個啥。
RMI(Remote Method Invocation)中文名:遠程方法調用,能夠讓在客戶端Java虛擬機上的對象像調用本地對象一樣調用服務端java 虛擬機中的對象上的方法。
基於RMI的配置有自動發現和手動發現 我這裏直說手動的 自動的還沒有嘗試過。
因爲這東西是JVM提供的,所以不需要任何其他 jar 可以直接使用
主要也是依賴於配置 幾乎不需要任何代碼侵入。 還是ehcache.xml

   <!-- 成員發現對象 cacheManagerPeerProviderFactory配置  -->
  <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
            properties="
        peerDiscovery=manual,  //手動發現
        remoteObjectPort=40003,  //監聽端口,這個東西如果你開了防火牆,就得手動配置,並且這個端口加入白名單,如果沒防火牆就不用了
        rmiUrls=//127.0.0.1:40002/articleCache     //監聽誰的cache 配置對方的如果有多個 中間用  |   隔開

"
  />
  <!--監聽對象  配置自己的  其中有自己的名字 自己的端口 連接超時時間等-->
  <cacheManagerPeerListenerFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
            properties="hostName=127.0.0.1, port=40001,
            socketTimeoutMillis=2000"/>

這兩個是放在 ehcache標籤裏面的
然後是放在cache標籤裏面的
<cache name="updateCache"
           eternal="true"
           maxElementsInMemory="10000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="0"
           timeToLiveSeconds="0"
           memoryStoreEvictionPolicy="LRU" >
        <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
                                   properties="replicateAsynchronously=true,
         replicatePuts=true,
         replicateUpdates=true,
         replicateUpdatesViaCopy=true,
         replicateRemovals=true"/>
        <!-- 用於在初始化緩存,以及自動設置 -->
        <!--<bootstrapCacheLoaderFactory class="net.sf.ehcache.bootstrap.BootstrapCacheLoaderFactory"/>-->
        <bootstrapCacheLoaderFactory
                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
    </cache>
<!-- 
   
   RMI緩存分佈同步查找 class使用net.sf.ehcache.distribution.RMICacheReplicatorFactory
        這個工廠支持以下屬性:
        replicatePuts=true | false – 當一個新元素增加到緩存中的時候是否要複製到其他的peers。默認是true。
        replicateUpdates=true | false – 當一個已經在緩存中存在的元素被覆蓋時是否要進行復制。默認是true。
        replicateRemovals= true | false – 當元素移除的時候是否進行復制。默認是true。
        replicateAsynchronously=true | false – 複製方式是異步的還是同步的 指定爲true時是異步的額,默認是true。
        replicatePutsViaCopy=true | false – 當一個新增元素被拷貝到其他的cache中時是否進行復制 指定爲true時爲複製,默認是true。
        replicateUpdatesViaCopy=true | false – 當一個元素被拷貝到其他的cache中時是否進行復制 指定爲true時爲複製,默認是true。
            asynchronousReplicationIntervalMillis=1000
-->

這個裏面配置很簡單 有幾個我使用中總結需要注意的
1、由於RMI需要通知,所以你改了對象後 需要調用put方法 告訴ehcache 你改了它,什麼不做不通知
2、同步和異步的問題,這裏我本機實驗,同步基於你對象的大小 所有需要同步的對象必須實現序列化,
序列化快自然就快。但是but 然而 如果 你配置的地址找不到人,rmiUrls=//127.0.0.1:40002/articleCache
這個東西,他會耗時很多 我本地是一個1s 注意是秒
這個底層實現 大約是這樣的
啓動時 會讀取你這個配置放進一個map 每次 監聽到有改動時遍歷map 每個去通知 所以也就是不支持動態增減機器的
誰掛了仍然可以正常使用,注意失敗log 是DUBUG級別的。
放一下所有兩個配置文件 僅供參考

server1:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <diskStore path="java.io.tmpdir/Tmp_EhCache" />
    <defaultCache eternal="false"
                  maxElementsInMemory="1000"
                  overflowToDisk="false"
                  diskPersistent="false"
                  timeToIdleSeconds="0"
                  timeToLiveSeconds="600"
                  memoryStoreEvictionPolicy="LRU" />

    <!-- server1 的cacheManagerPeerProviderFactory配置 -->
    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
            properties="
        peerDiscovery=manual,
        remoteObjectPort=40003,
        rmiUrls=//127.0.0.1:40002/articleCache|//127.0.0.1:40002/commentCache

"
    />
    <cacheManagerPeerListenerFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
            properties="hostName=127.0.0.1, port=40001,
            socketTimeoutMillis=2000"/>

    <cache name="articleCache"
           eternal="true"
           maxElementsInMemory="10000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="0"
           timeToLiveSeconds="0"
           memoryStoreEvictionPolicy="LRU" >
        <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
                                   properties="replicateAsynchronously=true,
         replicatePuts=true,
         replicateUpdates=true,
         replicateUpdatesViaCopy=true,
         replicateRemovals=true,
replicateAsynchronously=false"/>
        <!-- 用於在初始化緩存,以及自動設置 -->
        <!--<bootstrapCacheLoaderFactory class="net.sf.ehcache.bootstrap.BootstrapCacheLoaderFactory"/>-->
        <bootstrapCacheLoaderFactory
                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
    </cache>



    <cache name="commentCache"
           eternal="false"
           maxElementsInMemory="50000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="0"
           timeToLiveSeconds="0"
           memoryStoreEvictionPolicy="LRU" >
        <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
                                   properties="replicateAsynchronously=true,
         replicatePuts=true,
         replicateUpdates=true,
         replicateUpdatesViaCopy=true,
         replicateRemovals=true "/>
        <!-- 用於在初始化緩存,以及自動設置 -->
        <!--<bootstrapCacheLoaderFactory class="net.sf.ehcache.bootstrap.BootstrapCacheLoaderFactory"/>-->
        <bootstrapCacheLoaderFactory
                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
                properties="bootstrapAsynchronously=true, maximumChunkSizeBytes=5000000"/>
    </cache>
</ehcache>

server2:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <diskStore path="java.io.tmpdir/Tmp_EhCache" />
    <defaultCache eternal="false"
                  maxElementsInMemory="1000"
                  overflowToDisk="false"
                  diskPersistent="false"
                  timeToIdleSeconds="0"
                  timeToLiveSeconds="600"
                  memoryStoreEvictionPolicy="LRU" />

    <!-- server1 的cacheManagerPeerProviderFactory配置 -->
    <cacheManagerPeerProviderFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
            properties="
        peerDiscovery=manual,
        remoteObjectPort=40004,
        rmiUrls=//127.0.0.1:40001/articleCache|//127.0.0.1:40001/commentCache"
    />

    <cacheManagerPeerListenerFactory
            class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
            properties="hostName=127.0.0.1, port=40002,
            socketTimeoutMillis=2000"/>

    <cache name="articleCache"
           eternal="true"
           maxElementsInMemory="10000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="0"
           timeToLiveSeconds="0"
           memoryStoreEvictionPolicy="LRU" >
        <cacheEventListenerFactory
                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
                properties="replicateAsynchronously=true,
        replicatePuts=true,
        replicateUpdates=true,
        replicateUpdatesViaCopy=true,
         replicateAsynchronously=true,
        replicateRemovals=true "/>
        <!-- 用於在初始化緩存,以及自動設置 -->
        <!--<bootstrapCacheLoaderFactory class="net.sf.ehcache.bootstrap.BootstrapCacheLoaderFactory"/>-->
        <bootstrapCacheLoaderFactory
                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
                properties="bootstrapAsynchronously=true">
        </bootstrapCacheLoaderFactory>
    </cache>
    <cache name="commentCache"
           eternal="false"
           maxElementsInMemory="50000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="0"
           timeToLiveSeconds="0"
           memoryStoreEvictionPolicy="LRU" >
        <cacheEventListenerFactory
                class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
                properties="replicateAsynchronously=true,
        replicatePuts=true,
        replicateUpdates=true,
        replicateUpdatesViaCopy=true,
         replicateAsynchronously=true,
        replicateRemovals=true "/>
        <!-- 用於在初始化緩存,以及自動設置 -->
        <!--<bootstrapCacheLoaderFactory class="net.sf.ehcache.bootstrap.BootstrapCacheLoaderFactory"/>-->
        <bootstrapCacheLoaderFactory
                class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory"
               >
        </bootstrapCacheLoaderFactory>
    </cache>
</ehcache>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章