RMI 是 Java 的一種遠程方法調用技術,是一種點對點的基於 Java 對象的通訊方式。EhCache 從 1.2 版本開始就支持 RMI 方式的緩存集羣。在集羣環境中 EhCache 所有緩存對象的鍵和值都必須是可序列化的,也就是必須實現 java.io.Serializable 接口,這點在其它集羣方式下也是需要遵守的。
下圖是 RMI 集羣模式的結構圖:
採用 RMI 集羣模式時,集羣中的每個節點都是對等關係,並不存在主節點或者從節點的概念,因此節點間必須有一個機制能夠互相認識對方,必須知道其它節點的信息,包括主機地址、端口號等。EhCache 提供兩種節點的發現方式:手工配置和自動發現。手工配置方式要求在每個節點中配置其它所有節點的連接信息,一旦集羣中的節點發生變化時,需要對緩存進行重新配置。
由於 RMI 是 Java 中內置支持的技術,因此使用 RMI 集羣模式時,無需引入其它的 Jar 包,EhCache 本身就帶有支持 RMI 集羣的功能。使用 RMI 集羣模式需要在 ehcache.xml 配置文件中定義 cacheManagerPeerProviderFactory 節點。
分佈式同步緩存要讓這邊的cache知道對方的cache,叫做Peer Discovery(成員發現) EHCache實現成員發現的方式有兩種:
1、手動查找
A、 在ehcache.xml中配置PeerDiscovery成員發現對象
Server1配置,配置本地hostName、port是400001,分別監聽192.168.8.32:400002的mobileCache和192.168.5.231:400003 的mobileCache。注意這裏的mobileCache是緩存的名稱,分別對應着server2、server3的cache的配置。
<?xml version="1.0" encoding="gbk"?><ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd"><diskStore path="java.io.tmpdir"/><!--集羣多臺服務器中的緩存,這裏是要同步一些服務器的緩存server1 hostName:192.168.8.9 port:400001 cacheName:mobileCacheserver2 hostName:192.168.8.32 port:400002 cacheName:mobileCacheserver3 hostName:192.168.8.231 port:400003 cacheName:mobileCache注意:每臺要同步緩存的服務器的RMI通信socket端口都不一樣,在配置的時候注意設置--><!-- server1 的cacheManagerPeerProviderFactory配置 --><cacheManagerPeerProviderFactoryclass="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"properties="hostName=localhost,port=400001,socketTimeoutMillis=2000,peerDiscovery=manual,rmiUrls=//192.168.8.32:400002/mobileCache|//192.168.5.231:400003/mobileCache"/></ehcache>以上注意cacheManagerPeerProviderFactory元素出現的位置在diskStore下
同樣在你的另外2臺服務器上增加配置
Server2,配置本地host,port爲400002,分別同步192.168.8.9:400001的mobileCache和192.168.5.231:400003的mobileCache
<!-- server2 的cacheManagerPeerProviderFactory配置 --><cacheManagerPeerProviderFactoryclass="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"properties="hostName=localhost,port=400002,socketTimeoutMillis=2000,peerDiscovery=manual,rmiUrls=//192.168.8.9:400001/mobileCache|//192.168.5.231:400003/mobileCache"/>Server3,配置本地host,port爲400003,分別同步192.168.8.9:400001的mobileCache緩存和192.168.8.32:400002的mobileCache緩存
<!-- server3 的cacheManagerPeerProviderFactory配置 --><cacheManagerPeerProviderFactoryclass="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"properties="hostName=localhost,port=400003,socketTimeoutMillis=2000,peerDiscovery=manual,rmiUrls=//192.168.8.9:400001/mobileCache|//192.168.8.32:400002/mobileCache"/>這樣就在三臺不同的服務器上配置了手動查找cache的PeerProvider成員發現的配置了。 值得注意的是你在配置rmiUrls的時候要特別注意url不能重複出現,並且端口、地址都是對的。
如果指定,hostname將使用InetAddress.getLocalHost().getHostAddress()來得到。
警告:不要將localhost配置爲本地地址127.0.0.1,因爲它在網絡中不可見將會導致不能從遠程服務器接收信息從而不能複製。在同一臺機器上有多個CacheManager的時候,你應該只用localhost來配置。
B、 下面配置緩存和緩存同步監聽,需要在每臺服務器中的ehcache.xml文件中增加cache配置和cacheEventListenerFactory、cacheLoaderFactory的配置
<defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="30" timeToLiveSeconds="30" overflowToDisk="false"/><!--配置自定義緩存maxElementsInMemory:緩存中允許創建的最大對象數eternal:緩存中對象是否爲永久的,如果是,超時設置將被忽略,對象從不過期。timeToIdleSeconds:緩存數據空閒的最大時間,也就是說如果有一個緩存有多久沒有被訪問就會被銷燬,如果該值是 0 就意味着元素可以停頓無窮長的時間。timeToLiveSeconds:緩存數據存活的時間,緩存對象最大的的存活時間,超過這個時間就會被銷燬,這只能在元素不是永久駐留時有效,如果該值是0就意味着元素可以停頓無窮長的時間。overflowToDisk:內存不足時,是否啓用磁盤緩存。memoryStoreEvictionPolicy:緩存滿了之後的淘汰算法。每一個小時更新一次緩存(1小時過期)--><cache name="mobileCache"maxElementsInMemory="10000"eternal="false"overflowToDisk="true"timeToIdleSeconds="1800"timeToLiveSeconds="3600"memoryStoreEvictionPolicy="LFU"><!--RMI緩存分佈同步查找 class使用net.sf.ehcache.distribution.RMICacheReplicatorFactory這個工廠支持以下屬性:replicatePuts=true | false – 當一個新元素增加到緩存中的時候是否要複製到其他的peers。默認是true。replicateUpdates=true | false – 當一個已經在緩存中存在的元素被覆蓋時是否要進行復制。默認是true。replicateRemovals= true | false – 當元素移除的時候是否進行復制。默認是true。replicateAsynchronously=true | false – 複製方式是異步的指定爲true時,還是同步的,指定爲false時。默認是true。replicatePutsViaCopy=true | false – 當一個新增元素被拷貝到其他的cache中時是否進行復制指定爲true時爲複製,默認是true。replicateUpdatesViaCopy=true | false – 當一個元素被拷貝到其他的cache中時是否進行復制指定爲true時爲複製,默認是true。asynchronousReplicationIntervalMillis=1000--><!-- 監聽RMI同步緩存對象配置 註冊相應的的緩存監聽類,用於處理緩存事件,如put,remove,update,和expire --><cacheEventListenerFactoryclass="net.sf.ehcache.distribution.RMICacheReplicatorFactory"properties="replicateAsynchronously=true,replicatePuts=true,replicateUpdates=true,replicateUpdatesViaCopy=false,replicateRemovals=true "/><!-- 用於在初始化緩存,以及自動設置 --><bootstrapCacheLoaderFactory class="net.sf.ehcache.bootstrap.BootstrapCacheLoaderFactory"/></cache>
C、 這樣就完成了3臺服務器的配置,下面給出server1的完整的ehcache.xml的配置
<?xml version="1.0" encoding="gbk"?><ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd"><diskStore path="java.io.tmpdir"/><!--集羣多臺服務器中的緩存,這裏是要同步一些服務器的緩存server1 hostName:192.168.8.9 port:400001 cacheName:mobileCacheserver2 hostName:192.168.8.32 port:400002 cacheName:mobileCacheserver3 hostName:192.168.8.231 port:400003 cacheName:mobileCache注意每臺要同步緩存的服務器的RMI通信socket端口都不一樣,在配置的時候注意設置--><!-- server1 的cacheManagerPeerProviderFactory配置 --><cacheManagerPeerProviderFactoryclass="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"properties="hostName=localhost,port=400001,socketTimeoutMillis=2000,peerDiscovery=manual,rmiUrls=//192.168.8.32:400002/mobileCache|//192.168.5.231:400003/mobileCache"/><defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="30" timeToLiveSeconds="30" overflowToDisk="false"/><!--配置自定義緩存maxElementsInMemory:緩存中允許創建的最大對象數eternal:緩存中對象是否爲永久的,如果是,超時設置將被忽略,對象從不過期。timeToIdleSeconds:緩存數據空閒的最大時間,也就是說如果有一個緩存有多久沒有被訪問就會被銷燬,如果該值是 0 就意味着元素可以停頓無窮長的時間。timeToLiveSeconds:緩存數據存活的時間,緩存對象最大的的存活時間,超過這個時間就會被銷燬,這只能在元素不是永久駐留時有效,如果該值是0就意味着元素可以停頓無窮長的時間。overflowToDisk:內存不足時,是否啓用磁盤緩存。memoryStoreEvictionPolicy:緩存滿了之後的淘汰算法。每一個小時更新一次緩存(1小時過期)--><cache name="mobileCache"maxElementsInMemory="10000"eternal="false"overflowToDisk="true"timeToIdleSeconds="1800"timeToLiveSeconds="3600"memoryStoreEvictionPolicy="LFU"><!--RMI緩存分佈同步查找 class使用net.sf.ehcache.distribution.RMICacheReplicatorFactory這個工廠支持以下屬性:replicatePuts=true | false – 當一個新元素增加到緩存中的時候是否要複製到其他的peers。默認是true。replicateUpdates=true | false – 當一個已經在緩存中存在的元素被覆蓋時是否要進行復制。默認是true。replicateRemovals= true | false – 當元素移除的時候是否進行復制。默認是true。replicateAsynchronously=true | false – 複製方式是異步的指定爲true時,還是同步的,指定爲false時。默認是true。replicatePutsViaCopy=true | false – 當一個新增元素被拷貝到其他的cache中時是否進行復制指定爲true時爲複製,默認是true。replicateUpdatesViaCopy=true | false – 當一個元素被拷貝到其他的cache中時是否進行復制指定爲true時爲複製,默認是true。asynchronousReplicationIntervalMillis=1000--><!-- 監聽RMI同步緩存對象配置 註冊相應的的緩存監聽類,用於處理緩存事件,如put,remove,update,和expire --><cacheEventListenerFactoryclass="net.sf.ehcache.distribution.RMICacheReplicatorFactory"properties="replicateAsynchronously=true,replicatePuts=true,replicateUpdates=true,replicateUpdatesViaCopy=false,replicateRemovals=true "/><!-- 用於在初始化緩存,以及自動設置 --><bootstrapCacheLoaderFactory class="net.sf.ehcache.bootstrap.BootstrapCacheLoaderFactory"/></cache></ehcache>
2、自動發現
自動發現配置和手動查找的方式有一點不同,其他的地方都基本是一樣的。同樣在ehcache.xml中增加配置,配置如下
<!--搜索某個網段上的緩存timeToLive0是限制在同一個服務器1是限制在同一個子網32是限制在同一個網站64是限制在同一個region128是限制在同一個大洲255是不限制--><cacheManagerPeerProviderFactoryclass="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"properties="peerDiscovery=automatic, multicastGroupAddress=192.168.0.1,multicastGroupPort=400004, timeToLive=32"/>常見的問題
有一個Tomcat或者是JDK的bug,在tomcat啓動時如果tomcat的安裝路徑中有空格的話,在啓動時RMI監聽器會失敗。參見http://archives.java.sun.com/cgi-bin/wa?A2=ind0205&L=rmi-users&P=797和http://www.ontotext.com/kim/doc/sys-doc/faq-howto-bugs/known-bugs.html。
由於在Windows上安裝Tomcat默認是裝在“Program Files”文件夾裏的,所以這個問題經常發生。
自動的peer discovery與廣播息息相關。廣播可能被路由阻攔,像Xen和VMWare這種虛擬化的技術也可以阻攔廣播。如果這些都打開了,你可能還在要將你的網卡的相關配置打開。一個簡單的辦法可以告訴廣播是否有效,
那就是使用ehcache remote debugger來看“心跳”是否可用。
你可以通過設置badly misnamed time to live來控制廣播傳播的距離。用廣播IP協議時,timeToLive的值指的是數據包可以傳遞的域或是範圍。約定如下:
0是限制在同一個服務器
1是限制在同一個子網
32是限制在同一個網站
64是限制在同一個region
128是限制在同一個大洲
255是不限制
譯者按:上面這些資料翻譯的不夠準確,請讀者自行尋找原文理解吧。
在Java實現中默認值是1,也就是在同一個子網中傳播。改變timeToLive屬性可以限制或是擴展傳播的範圍。
其它可能造成緩存同步失敗的問題
ehcache 集羣通過rmi組播 Multicast進行同步的,如果發現更新數據集羣沒有同步的話,可能原因:
1、開啓防火牆問題
2、沒有開啓組播功能
windows下默認是開啓的。
Linux下可能沒有開啓
route add -net 224.0.0.0 netmask 240.0.0.0 dev eth1
3、Linux服務器的主機名(hostname)在/etc/hosts文件中設置的IP地址爲127.0.0.1。檢測方法爲:
home>hostname -i
如果返回值爲127.0.0.1,那麼這就是問題。
解決方法:修改/etc/hosts文件,127.0.0.1 IP不能對應主機名,它對應localhost就可以了。
修改之前的hosts文件:
127.0.0.1 OAServer1 localhost localhost.localhost
修改之後的hosts文件:
127.0.0.1 localhost localhost.localhost
其他的配置和手動查找方式的配置是一樣的,這裏就不再贅述了。關於ehcache的其他緩存配置方式這裏將不再介紹,大家可以自己去研究。可以參考:
官方文檔:http://www.ehcache.org/documentation/user-guide/cache-topologies#using-a-cache-server
ibm developerworks文檔:http://www.ibm.com/developerworks/cn/java/j-lo-ehcache/index.html
有不懂的,或者更好的見解可以隨時交流!每天都會看的。