ehcache是一个开源的Java本地缓存框架,同时以replication的形式提供分布式缓存管理。这里介绍下使用RMI手动配置的方法在一个JVM里面启动多个CacheManager来实现分布式缓存。
这里配置3个CacheManager,由于只有一台机器,所以实在一个JVM里面使用3个CacheManager的方法来模拟分布式。
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true" monitoring="autodetect" dynamicConfig="true" name="nodeA"> <cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" properties="peerDiscovery=manual, rmiUrls=//localhost:40002/cache1|//localhost:40003/cache1" /> <cacheManagerPeerListenerFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" properties="hostName=localhost, port=40001, socketTimeoutMillis=2000" /> <cache name="cache1" maxEntriesLocalHeap="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>
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true" monitoring="autodetect" dynamicConfig="true" name="nodeB"> <cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" properties="peerDiscovery=manual, rmiUrls=//localhost:40001/cache1|//localhost:40003/cache1" /> <cacheManagerPeerListenerFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" properties="hostName=localhost, port=40002, socketTimeoutMillis=2000" /> <cache name="cache1" maxEntriesLocalHeap="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>
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true" monitoring="autodetect" dynamicConfig="true" name="nodeC"> <cacheManagerPeerProviderFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory" properties="peerDiscovery=manual, rmiUrls=//localhost:40001/cache1|//localhost:40002/cache1" /> <cacheManagerPeerListenerFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory" properties="hostName=localhost, port=40003, socketTimeoutMillis=2000" /> <cache name="cache1" maxEntriesLocalHeap="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>
package com.suifengfei.learn.ehcachelearn.replication; import net.sf.ehcache.Cache; import net.sf.ehcache.CacheManager; import net.sf.ehcache.Element; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class RmiReplication { private final static Logger logger = LoggerFactory.getLogger(RmiReplication.class); private CacheManager nodeA ; private CacheManager nodeB ; private CacheManager nodeC ; public void setup(){ nodeA = CacheManager.newInstance("src/main/resources/ehcache-rmi-replication-nodeA.xml"); nodeB = CacheManager.newInstance("src/main/resources/ehcache-rmi-replication-nodeB.xml"); nodeC = CacheManager.newInstance("src/main/resources/ehcache-rmi-replication-nodeC.xml"); } public void shutdown(){ if( null != nodeA ){ nodeA.shutdown(); } if( null != nodeB ){ nodeB.shutdown(); } if( null != nodeC ){ nodeC.shutdown(); } } public static void main(String[] args) { RmiReplication rmiReplication = new RmiReplication(); rmiReplication.setup(); try { rmiReplication.simpleTest(); } catch (InterruptedException e) { e.printStackTrace(); } rmiReplication.shutdown(); } private void simpleTest() throws InterruptedException{ Cache cacheA1 = nodeA.getCache("cache1"); Cache cacheB1 = nodeB.getCache("cache1"); Cache cacheC1 = nodeC.getCache("cache1"); Element e1 = new Element("a", "hello") ; cacheA1.put(e1); Thread.sleep(2000); Element e1_B = cacheB1.get("a"); if( null != e1_B ){ String e1V = (String)e1_B.getValue(); logger.info("e1 value from nodeB:" + e1V); } Element e1_C = cacheC1.get("a"); if( null != e1_C ){ String e1V = (String)e1_C.getValue(); logger.info("e1 value from nodeC:" + e1V); } } }
[22 13:02:48,411 INFO ] [main] replication.RmiReplication - e1 value from nodeB:hello [22 13:02:48,411 INFO ] [main] replication.RmiReplication - e1 value from nodeC:hello
在配置ehcache的时候,有一点需要注意,需要配置好ehcache元素的name属性,虽然这个属性是一个optinal的,我们看一下官方文档
CacheManager.newInstance(Configuration configuration) – Create a new CacheManager or return the existing one named in the configuration.
也就说每一个CacheManager都有一个name,如果不配置name属性的话,这个name会是一个相同的默认值,在测试代码中虽然nodeA,nodeB,nodeC加载了3个不同的配置,但是由于name都是相同的,返回的nodeB和nodeC其实和nodeA引用了相同的对象,在运行到缓存操作的时候,由于调用不到对方的RMI接口服务,会报错。
[22 13:08:44,148 DEBUG] [Replication Thread] distribution.ManualRMICacheManagerPeerProvider - Looking up rmiUrl //localhost:40002/cache1 through exception Connection refused to host: localhost; nested exception is: java.net.ConnectException: Connection refused: connect. This may be normal if a node has gone offline. Or it may indicate network connectivity difficulties java.rmi.ConnectException: Connection refused to host: localhost; nested exception is: java.net.ConnectException: Connection refused: connect
通过下面的CachaManager的实现也可以看出来对于这个name的判断
private static CacheManager newInstance(Configuration configuration, String msg) throws CacheException { synchronized (CacheManager.class) { String name = configuration.getName(); if (name == null) { name = DEFAULT_NAME; } CacheManager cacheManager = CACHE_MANAGERS_MAP.get(name); if (cacheManager == null) { LOG.debug(msg); cacheManager = new CacheManager(configuration); } return cacheManager; } }