在一个JVM里实现ehcache replciation分布式缓存

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;
        }
    }
 

猜你喜欢

转载自isuifengfei.iteye.com/blog/1733182