EhCache 是一個純 Java 的進程內緩存框架,具備快速、精幹等特色,是 Hibernate 中默認的 CacheProvider。java
下圖是 EhCache 在應用程序中的位置:編程
EhCache 的主要特性有:緩存
因爲 EhCache 是進程中的緩存系統,一旦將應用部署在集羣環境中,每個節點維護各自的緩存數據,當某個節點對緩存數據進行更新,這些更新的數據沒法在其它節點中共享,這不只會下降節點運行的效率,並且會致使數據不一樣步的狀況發生。例如某個網站採用 A、B 兩個節點做爲集羣部署,當 A 節點的緩存更新後,而 B 節點緩存還沒有更新就可能出現用戶在瀏覽頁面的時候,一會是更新後的數據,一會是還沒有更新的數據,儘管咱們也能夠經過 Session Sticky 技術來將用戶鎖定在某個節點上,但對於一些交互性比較強或者是非 Web 方式的系統來講,Session Sticky 顯然不太適合。因此就須要用到 EhCache 的集羣解決方案。安全
EhCache 從 1.7 版本開始,支持五種集羣方案,分別是:服務器
本文主要介紹其中的三種最爲經常使用集羣方式,分別是 RMI、JGroups 以及 EhCache Server 。架構
RMI 是 Java 的一種遠程方法調用技術,是一種點對點的基於 Java 對象的通信方式。EhCache 從 1.2 版本開始就支持 RMI 方式的緩存集羣。在集羣環境中 EhCache 全部緩存對象的鍵和值都必須是可序列化的,也就是必須實現 java.io.Serializable 接口,這點在其它集羣方式下也是須要遵照的。框架
下圖是 RMI 集羣模式的結構圖:curl
採用 RMI 集羣模式時,集羣中的每一個節點都是對等關係,並不存在主節點或者從節點的概念,所以節點間必須有一個機制可以互相認識對方,必須知道其它節點的信息,包括主機地址、端口號等。EhCache 提供兩種節點的發現方式:手工配置和自動發現。手工配置方式要求在每一個節點中配置其它全部節點的鏈接信息,一旦集羣中的節點發生變化時,須要對緩存進行從新配置。socket
因爲 RMI 是 Java 中內置支持的技術,所以使用 RMI 集羣模式時,無需引入其它的 Jar 包,EhCache 自己就帶有支持 RMI 集羣的功能。使用 RMI 集羣模式須要在 ehcache.xml 配置文件中定義 cacheManagerPeerProviderFactory 節點。假設集羣中有兩個節點,分別對應的 RMI 綁定信息是:編程語言
節點 1 | 192.168.0.11 | 4567 | /oschina_cache |
---|---|---|---|
節點 2 | 192.168.0.12 | 4567 | /oschina_cache |
節點 3 | 192.168.0.13 | 4567 | /oschina_cache |
那麼對應的手工配置信息以下:
<cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" properties="hostName=localhost, port=4567, socketTimeoutMillis=2000, peerDiscovery=manual, rmiUrls=//192.168.0.12:4567/oschina_cache|//192.168.0.13:4567/oschina_cache" />
其它節點配置相似,只需把 rmiUrls 中的兩個 IP 地址換成另外兩個節點對應的 IP 地址便可。
接下來在須要進行緩存數據複製的區域(Region)上配置以下便可:
<cache name="sampleCache2" maxElementsInMemory="10" eternal="false" timeToIdleSeconds="100" timeToLiveSeconds="100" overflowToDisk="false"> <cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" properties="replicateAsynchronously=true, replicatePuts=true, replicateUpdates=true, replicateUpdatesViaCopy=false, replicateRemovals=true "/> </cache>
具體每一個參數表明的意義請參考 EhCache 的手冊,此處再也不詳細說明。
EhCache 的 RMI 集羣模式還有另一種節點發現方式,就是經過多播( multicast )來維護集羣中的全部有效節點。這也是最爲簡單並且靈活的方式,與手工模式不一樣的是,每一個節點上的配置信息都相同,大大方便了節點的部署,避免人爲的錯漏出現。
在上述三個節點的例子中,配置以下:
<cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" properties="peerDiscovery=automatic, multicastGroupAddress=230.0.0.1, multicastGroupPort=4446, timeToLive=32" />
其中須要指定節點發現模式 peerDiscovery 值爲 automatic 自動;同時組播地址能夠指定 D 類 IP 地址空間,範圍從 224.0.1.0 到 238.255.255.255 中的任何一個地址。
EhCache 從 1.5. 版本開始增長了 JGroups 的分佈式集羣模式。與 RMI 方式相比較, JGroups 提供了一個很是靈活的協議棧、可靠的單播和多播消息傳輸,主要的缺點是配置複雜以及一些協議棧對第三方包的依賴。
JGroups 也提供了基於 TCP 的單播 ( Unicast ) 和基於 UDP 的多播 ( Multicast ) ,對應 RMI 的手工配置和自動發現。使用單播方式須要指定其它節點的主機地址和端口,下面是兩個節點,並使用了單播方式的配置:
<cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.jgroups.JGroupsCacheManagerPeerProviderFactory" properties="connect=TCP(start_port=7800): TCPPING(initial_hosts=host1[7800],host2[7800];port_range=10;timeout=3000; num_initial_members=3;up_thread=true;down_thread=true): VERIFY_SUSPECT(timeout=1500;down_thread=false;up_thread=false): pbcast.NAKACK(down_thread=true;up_thread=true;gc_lag=100; retransmit_timeout=3000): pbcast.GMS(join_timeout=5000;join_retry_timeout=2000;shun=false; print_local_addr=false;down_thread=true;up_thread=true)" propertySeparator="::" />
使用多播方式配置以下:
<cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.jgroups.JGroupsCacheManagerPeerProviderFactory" properties="connect=UDP(mcast_addr=231.12.21.132;mcast_port=45566;):PING: MERGE2:FD_SOCK:VERIFY_SUSPECT:pbcast.NAKACK:UNICAST:pbcast.STABLE:FRAG:pbcast.GMS" propertySeparator="::" />
從上面的配置來看,JGroups 的配置要比 RMI 複雜得多,但也提供更多的微調參數,有助於提高緩存數據複製的性能。詳細的 JGroups 配置參數的具體意義可參考 JGroups 的配置手冊。
JGroups 方式對應緩存節點的配置信息以下:
<cache name="sampleCache2" maxElementsInMemory="10" eternal="false" timeToIdleSeconds="100" timeToLiveSeconds="100" overflowToDisk="false"> <cacheEventListenerFactory class="net.sf.ehcache.distribution.jgroups.JGroupsCacheReplicatorFactory" properties="replicateAsynchronously=true, replicatePuts=true, replicateUpdates=true, replicateUpdatesViaCopy=false, replicateRemovals=true" /> </cache>
使用 JGroups 須要引入 JGroups 的 Jar 包以及 EhCache 對 JGroups 的封裝包 ehcache-jgroupsreplication-xxx.jar 。
在一些啓用了 IPv6 的電腦中,常常啓動的時候報以下錯誤信息:
java.lang.RuntimeException: the type of the stack (IPv6) and the user supplied addresses (IPv4) don't match: /231.12.21.132.
解決的辦法是增長 JVM 參數:-Djava.net.preferIPv4Stack=true。若是是 Tomcat 服務器,可在 catalina.bat 或者 catalina.sh 中增長以下環境變量便可:
SET CATALINA_OPTS=-Djava.net.preferIPv4Stack=true
通過實際測試發現,集羣方式下的緩存數據均可以在 1 秒鐘以內完成到其節點的複製。
與前面介紹的兩種集羣方案不一樣的是, EhCache Server 是一個獨立的緩存服務器,其內部使用 EhCache 作爲緩存系統,可利用前面提到的兩種方式進行內部集羣。對外提供編程語言無關的基於 HTTP 的 RESTful 或者是 SOAP 的數據緩存操做接口。
下面是 EhCache Server 提供的對緩存數據進行操做的方法:
OPTIONS /{cache}}
獲取某個緩存的可用操做的信息。
HEAD /{cache}/{element}
獲取緩存中某個元素的 HTTP 頭信息,例如:
curl --head http://localhost:8080/ehcache/rest/sampleCache2/2
EhCache Server 返回的信息以下:
HTTP/1.1 200 OK X-Powered-By: Servlet/2.5 Server: GlassFish/v3 Last-Modified: Sun, 27 Jul 2008 08:08:49 GMT ETag: "1217146129490" Content-Type: text/plain; charset=iso-8859-1 Content-Length: 157 Date: Sun, 27 Jul 2008 08:17:09 GMT
GET /{cache}/{element}
讀取緩存中某個數據的值。
PUT /{cache}/{element}
寫緩存。
因爲這些操做都是基於 HTTP 協議的,所以你能夠在任何一種編程語言中使用它,例如 Perl、PHP 和 Ruby 等等。
下圖是 EhCache Server 在應用中的架構:
EhCache Server 同時也提供強大的安全機制、監控功能。在數據存儲方面,最大的 Ehcache 單實例在內存中能夠緩存 20GB。最大的磁盤能夠緩存 100GB。經過將節點整合在一塊兒,這樣緩存數據就能夠跨越節點,以此得到更大的容量。將緩存 20GB 的 50 個節點整合在一塊兒就是 1TB 了。
以上咱們介紹了三種 EhCache 的集羣方案,除了第三種跨編程語言的方案外,EhCache 的集羣對應用程序的代碼編寫都是透明的,程序人員無需考慮緩存數據是如何複製到其它節點上。既保持了代碼的輕量級,同時又支持龐大的數據集羣。EhCache 可謂是深刻人心。
2009 年年中,Terracotta 宣佈收購 EhCache 產品。Terracotta 公司的產品 Terracotta 是一個 JVM 級的開源羣集框架,提供 HTTP Session 複製、分佈式緩存、POJO 羣集、跨越集羣的 JVM 來實現分佈式應用程序協調。最近 EhCache 主要的改進都集中在跟 Terracotta 框架的集成上,這是一個真正意義上的企業級緩存解決方案。