spring ehcache缓存集群自动配置(rmi方式)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/v2sking/article/details/78995310

众所周知 ehcache是支持通过rmi的缓存同步机制,ehcache.xml只需如下配置即可:

<?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">
	<diskStore path="java.io.tmpdir" />
	
	<cacheManagerPeerProviderFactory
	class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
	properties="peerDiscovery=automatic, multicastGroupAddress=224.1.1.1,
				multicastGroupPort=1000, timeToLive=32" />
				
	<cacheManagerPeerListenerFactory
		class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
		properties="hostName=127.0.0.1,port=1000,socketTimeoutMillis=120000" />
	<!-- 默认缓存 -->
	<defaultCache maxElementsInMemory="1000" eternal="true"
		timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
		diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
		diskPersistent="true" diskExpiryThreadIntervalSeconds="120"
		memoryStoreEvictionPolicy="LRU">
	</defaultCache>
	
	<!-- demo缓存 -->
	<cache name="user" maxElementsInMemory="1000" eternal="false"
		timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true"
		diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000"
		diskPersistent="false" diskExpiryThreadIntervalSeconds="120"
		memoryStoreEvictionPolicy="LRU">
		<cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" />
		<!-- 用于在初始化缓存,以及自动设置 -->
		<bootstrapCacheLoaderFactory  class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" />
	</cache>
	
</ehcache>
所有的这些属性都是配置在ehcache.xml里,包含广播地址,监听地址,缓存监听工厂等。

和集群有关的元素为:

cacheManagerPeerProviderFactory
cacheManagerPeerListenerFactory
cacheEventListenerFactory

生产环境下,一旦地址端口,每台机器都需要调整ehcache.xml配置,增加了运维工作量。

该篇提供了一个简化版的集群配置,简化ehcache.xml,将相关缓存广播监听事件工厂全部自动配置,项目只配置具体的cache元素(因项目环境cache元素基本不会改变,这里简单实现,其实可以连ehcache.xml都不需要,全部采用自动配置)



定义ClusterEhCacheManagerFactoryBean(ehcache集群缓存管理工厂bean)实现FactoryBean<CacheManager>, InitializingBean, DisposableBean 接口

主要内容如下:

注入ehClusterConfig

@Autowired
private ClusterEhCacheConfig ehClusterConfig;
 判断是否集群,如果是,分别配置 
 

缓存管理注册提供者工厂(configCacheManagerPeerProviderFactory)

配置缓存同步管理监听工厂(configCacheManagerPeerListenerFactory)

配置缓存事件监听工厂(configCacheEventListenerFactory)

if(this.ehClusterConfig.getEnableCluster()){
			configCacheManagerPeerProviderFactory(configuration);
			configCacheManagerPeerListenerFactory(configuration);
			configCacheEventListenerFactory(configuration);
		}
/**
	 * 
	* @Title: configCacheManagerPeerProviderFactory
	* @Description:  配置缓存管理注册提供者工厂
	* @return void    返回类型
	* @param configuration
	 */
	public void configCacheManagerPeerProviderFactory(Configuration configuration) {
		LOG.debug("ehClusterConfig:" + ehClusterConfig.toString());
		String peerDiscovery = ehClusterConfig.getPeerDiscovery();
		LOG.debug("rmi方式:" + peerDiscovery);
		if (StringUtils.isEmpty(peerDiscovery)) {
			return;
		}
		if ("automatic".equals(peerDiscovery)) {
			configAutomaticCacheManagerPeerProviderFactory(configuration);
		} else if ("manual".equals(peerDiscovery)) {
			configManualCacheManagerPeerProviderFactory(configuration);
		} else {
			LOG.error("invalid peerDiscovery:" + peerDiscovery);
			return;
		}
	}

peerDiscovery有自动和人工两种方式

先看自动

/**
	 * 
	* @Title: configAutomaticCacheManagerPeerProviderFactory
	* @Description: 配置缓存管理自动注册提供者工厂
	* @return void    返回类型
	* @param configuration
	 */
	private void configAutomaticCacheManagerPeerProviderFactory(Configuration configuration) {
		configuration.cacheManagerPeerProviderFactory(new FactoryConfiguration<FactoryConfiguration<?>>()
				.className(RMICacheManagerPeerProviderFactory.class.getName())
				.properties("peerDiscovery=automatic,multicastGroupAddress="
						+ ehClusterConfig.getMulticastGroupAddress().trim() + ",multicastGroupPort="
						+ ehClusterConfig.getMulticastGroupPort() + ",timeToLive=32"));
	}
主要从配置中获取广播地址和端口等。

再看手工方式

/**
	 * 
	* @Title: configManualCacheManagerPeerProviderFactory
	* @Description: 配置缓存管理手工注册提供者工厂
	* @return void    返回类型
	* @param configuration
	 */
	private void configManualCacheManagerPeerProviderFactory(Configuration configuration) {
		String rmiUrls = ehClusterConfig.getRmiUrls().trim();
		StringTokenizer stringTokenizer = new StringTokenizer(rmiUrls, "|");
		Set<String> cacheConfigurationsKeySet = configuration.getCacheConfigurationsKeySet();
		StringBuilder rmiUrlsStr = new StringBuilder();
		while (stringTokenizer.hasMoreTokens()) {
			String rmiUrl = stringTokenizer.nextToken();
			rmiUrl = rmiUrl.trim();
			for (String key : cacheConfigurationsKeySet) {
				rmiUrlsStr.append("//").append(rmiUrl).append("/").append(key).append("|");
			}
		}
		rmiUrlsStr = rmiUrlsStr.deleteCharAt(rmiUrlsStr.length() - 1);
		LOG.debug("last rmiUrls:" + rmiUrlsStr.toString());
		configuration.cacheManagerPeerProviderFactory(new FactoryConfiguration<FactoryConfiguration<?>>()
				.className(RMICacheManagerPeerProviderFactory.class.getName())
				.properties("peerDiscovery=manual,rmiUrls=" + rmiUrlsStr));
	}
配置需要通知的rmi地址


下面配置缓存同步管理监听工厂:

/**
	 * 
	* @Title: configCacheManagerPeerListenerFactory
	* @Description:配置缓存同步管理监听工厂
	* @return void    返回类型
	* @param configuration
	 */
	private void configCacheManagerPeerListenerFactory(Configuration configuration) {
		configuration.cacheManagerPeerListenerFactory(new FactoryConfiguration<FactoryConfiguration<?>>()
				.className(RMICacheManagerPeerListenerFactory.class.getName())
				.properties("hostName=" + ehClusterConfig.getHostName().trim() + ",port="
						+ ehClusterConfig.getPort() + ",socketTimeoutMillis=2000"));
	}

接下来配置缓存时间监听工厂

/**
	 * 
	* @Title: configCacheEventListenerFactory
	* @Description: 配置缓存事件监听工厂
	* @return void    返回类型
	* @param configuration
	 */
	private void configCacheEventListenerFactory(Configuration configuration) {
		Properties defaultRMICacheReplicatorFactoryProperties = PropertyUtil
				.parseProperties(defaultRMICacheReplicatorFactoryPropertiesName, ",");
		Map<String, CacheConfiguration> cacheConfigurations = configuration.getCacheConfigurations();
		//先过滤掉无需缓存同步的cache
		Set<String> filterNoSyncCache = filterNoSyncCache(configuration.getCacheConfigurationsKeySet());
		
		for (String key : filterNoSyncCache) {
			
			CacheConfiguration cacheConfiguration = cacheConfigurations.get(key);

			boolean hasRMICacheReplicatorFactory = false;
			@SuppressWarnings("unchecked")
			List<CacheEventListenerFactoryConfiguration> cacheEventListenerConfigurations = cacheConfiguration
					.getCacheEventListenerConfigurations();
			//已经配置缓存监听情况
			if (cacheEventListenerConfigurations != null && !cacheEventListenerConfigurations.isEmpty()) {
				for (CacheEventListenerFactoryConfiguration c1 : cacheEventListenerConfigurations) {
					String className = c1.getFullyQualifiedClassPath();
					if (className.equals(RMICacheReplicatorFactory.class.getName())) {
						hasRMICacheReplicatorFactory = true;
						Properties parseProperties = PropertyUtil.parseProperties(c1.getProperties(),
								c1.getPropertySeparator());
						//属性为空 设置默认
						if (parseProperties == null) {
							c1.properties(defaultRMICacheReplicatorFactoryPropertiesName);
							continue;
						}
						//属性不为空 ,和默认属性合并
						Enumeration<?> propertyNames = parseProperties.propertyNames();
						while (propertyNames.hasMoreElements()) {
							String key1 = (String) propertyNames.nextElement();
							String property = parseProperties.getProperty(key1);
							if (StringUtils.hasText(property)) {
								defaultRMICacheReplicatorFactoryProperties.put(key1, property);
							}
						}
						//重新设置合并后的属性
						Enumeration<?> propertyNames1 = defaultRMICacheReplicatorFactoryProperties.propertyNames();
						StringBuilder sb = new StringBuilder();
						while (propertyNames1.hasMoreElements()) {
							String key1 = (String) propertyNames1.nextElement();
							String property = defaultRMICacheReplicatorFactoryProperties.getProperty(key1);
							sb.append(key1).append("=").append(property).append(",");
						}
						c1.setProperties(sb.substring(0, sb.length() - 1).toString());
					}
				}
			}
			//未配置缓存监听情况 设置默认RMICacheReplicatorFactory即属性
			if (!hasRMICacheReplicatorFactory) {
				CacheEventListenerFactoryConfiguration cacheEventListenerFactoryConfiguration = new CacheEventListenerFactoryConfiguration();
				cacheEventListenerFactoryConfiguration.className(RMICacheReplicatorFactory.class.getName());
				cacheEventListenerFactoryConfiguration.properties(defaultRMICacheReplicatorFactoryPropertiesName);
				cacheConfiguration.addCacheEventListenerFactory(cacheEventListenerFactoryConfiguration);
			}

		}
	}



ehcache集群配置参考:

http://blog.csdn.net/tang06211015/article/details/52281551


后续有空会做成一个spring-boot-ehcache-cluster-starter上传


以下4个文件和ehcache.xml分别拷贝到到两个项目中,或者打jar包引入,本地启动服务,因为默认是自动配置方式,无需配置具体广播和监听地址,即达成了ehcache集群自动配置,

代码清单:

ClusterEhCacheManagerFactoryBean

package com.wisedu.zzfw.template.common.cache.cluster;

import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.ehcache.EhCacheManagerUtils;
import org.springframework.core.io.Resource;
import org.springframework.util.StringUtils;

import net.sf.ehcache.CacheException;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.config.CacheConfiguration.CacheEventListenerFactoryConfiguration;
import net.sf.ehcache.config.Configuration;
import net.sf.ehcache.config.ConfigurationFactory;
import net.sf.ehcache.config.FactoryConfiguration;
import net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory;
import net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory;
import net.sf.ehcache.distribution.RMICacheReplicatorFactory;
import net.sf.ehcache.util.PropertyUtil;

/**
 * 扩展了EhCacheManagerFactoryBean 增加通过配置文件rmi 集群配置
 * 
 * @author luanhy
 *
 */
public class ClusterEhCacheManagerFactoryBean implements FactoryBean<CacheManager>, InitializingBean, DisposableBean {

	private static final Logger LOG = LoggerFactory.getLogger(ClusterEhCacheManagerFactoryBean.class);

	private Resource configLocation;

	private String cacheManagerName;

	private boolean acceptExisting = false;

	private boolean shared = false;

	private CacheManager cacheManager;

	private boolean locallyManaged = true;
	
	@Autowired
	private ClusterEhCacheConfig ehClusterConfig;

	private String defaultRMICacheReplicatorFactoryPropertiesName = "replicatePuts=true,replicateUpdates=true,replicateRemovals=true,replicateAsynchronously=true,replicatePutsViaCopy=true,replicateUpdatesViaCopy=true,asynchronousReplicationIntervalMillis=true";


	/**
	 * Set the location of the EhCache config file. A typical value is
	 * "/WEB-INF/ehcache.xml".
	 * <p>
	 * Default is "ehcache.xml" in the root of the class path, or if not found,
	 * "ehcache-failsafe.xml" in the EhCache jar (default EhCache
	 * initialization).
	 * 
	 * @see net.sf.ehcache.CacheManager#create(java.io.InputStream)
	 * @see net.sf.ehcache.CacheManager#CacheManager(java.io.InputStream)
	 */
	public void setConfigLocation(Resource configLocation) {
		this.configLocation = configLocation;
	}

	/**
	 * Set the name of the EhCache CacheManager (if a specific name is desired).
	 * 
	 * @see net.sf.ehcache.CacheManager#setName(String)
	 */
	public void setCacheManagerName(String cacheManagerName) {
		this.cacheManagerName = cacheManagerName;
	}

	/**
	 * Set whether an existing EhCache CacheManager of the same name will be
	 * accepted for this EhCacheManagerFactoryBean setup. Default is "false".
	 * <p>
	 * Typically used in combination with {@link #setCacheManagerName
	 * "cacheManagerName"} but will simply work with the default CacheManager
	 * name if none specified. All references to the same CacheManager name (or
	 * the same default) in the same ClassLoader space will share the specified
	 * CacheManager then.
	 * 
	 * @see #setCacheManagerName #see #setShared
	 * @see net.sf.ehcache.CacheManager#getCacheManager(String)
	 * @see net.sf.ehcache.CacheManager#CacheManager()
	 */
	public void setAcceptExisting(boolean acceptExisting) {
		this.acceptExisting = acceptExisting;
	}

	/**
	 * Set whether the EhCache CacheManager should be shared (as a singleton at
	 * the ClassLoader level) or independent (typically local within the
	 * application). Default is "false", creating an independent local instance.
	 * <p>
	 * <b>NOTE:</b> This feature allows for sharing this
	 * EhCacheManagerFactoryBean's CacheManager with any code calling
	 * <code>CacheManager.create()</code> in the same ClassLoader space, with no
	 * need to agree on a specific CacheManager name. However, it only supports
	 * a single EhCacheManagerFactoryBean involved which will control the
	 * lifecycle of the underlying CacheManager (in particular, its shutdown).
	 * <p>
	 * This flag overrides {@link #setAcceptExisting "acceptExisting"} if both
	 * are set, since it indicates the 'stronger' mode of sharing.
	 * 
	 * @see #setCacheManagerName
	 * @see #setAcceptExisting
	 * @see net.sf.ehcache.CacheManager#create()
	 * @see net.sf.ehcache.CacheManager#CacheManager()
	 */
	public void setShared(boolean shared) {
		this.shared = shared;
	}

	@Override
	public void afterPropertiesSet() throws CacheException {
		LOG.info("Initializing EhCache CacheManager");
		Configuration configuration = this.configLocation != null
				? EhCacheManagerUtils.parseConfiguration(this.configLocation)
				: ConfigurationFactory.parseConfiguration();
		if (this.cacheManagerName != null) {
			configuration.setName(this.cacheManagerName);
		}
		
		if(this.ehClusterConfig.getEnableCluster()){
			configCacheManagerPeerProviderFactory(configuration);
			configCacheManagerPeerListenerFactory(configuration);
			configCacheEventListenerFactory(configuration);
		}
	
		if (this.shared) {
			// Old-school EhCache singleton sharing...
			// No way to find out whether we actually created a new CacheManager
			// or just received an existing singleton reference.
			this.cacheManager = CacheManager.create(configuration);
		} else if (this.acceptExisting) {
			// EhCache 2.5+: Reusing an existing CacheManager of the same name.
			// Basically the same code as in CacheManager.getInstance(String),
			// just storing whether we're dealing with an existing instance.
			synchronized (CacheManager.class) {
				this.cacheManager = CacheManager.getCacheManager(this.cacheManagerName);
				if (this.cacheManager == null) {
					this.cacheManager = new CacheManager(configuration);
				} else {
					this.locallyManaged = false;
				}
			}
		} else {
			// Throwing an exception if a CacheManager of the same name exists
			// already...
			this.cacheManager = new CacheManager(configuration);
		}

	}
	
	/**
	 * 
	* @Title: configCacheManagerPeerProviderFactory
	* @Description:  配置缓存管理注册提供者工厂
	* @return void    返回类型
	* @param configuration
	 */
	public void configCacheManagerPeerProviderFactory(Configuration configuration) {
		LOG.debug("ehClusterConfig:" + ehClusterConfig.toString());
		String peerDiscovery = ehClusterConfig.getPeerDiscovery();
		LOG.debug("rmi方式:" + peerDiscovery);
		if (StringUtils.isEmpty(peerDiscovery)) {
			return;
		}
		if ("automatic".equals(peerDiscovery)) {
			configAutomaticCacheManagerPeerProviderFactory(configuration);
		} else if ("manual".equals(peerDiscovery)) {
			configManualCacheManagerPeerProviderFactory(configuration);
		} else {
			LOG.error("invalid peerDiscovery:" + peerDiscovery);
			return;
		}
	}

	/**
	 * 
	* @Title: configManualCacheManagerPeerProviderFactory
	* @Description: 配置缓存管理手工注册提供者工厂
	* @return void    返回类型
	* @param configuration
	 */
	private void configManualCacheManagerPeerProviderFactory(Configuration configuration) {
		String rmiUrls = ehClusterConfig.getRmiUrls().trim();
		StringTokenizer stringTokenizer = new StringTokenizer(rmiUrls, "|");
		Set<String> cacheConfigurationsKeySet = configuration.getCacheConfigurationsKeySet();
		StringBuilder rmiUrlsStr = new StringBuilder();
		while (stringTokenizer.hasMoreTokens()) {
			String rmiUrl = stringTokenizer.nextToken();
			rmiUrl = rmiUrl.trim();
			for (String key : cacheConfigurationsKeySet) {
				rmiUrlsStr.append("//").append(rmiUrl).append("/").append(key).append("|");
			}
		}
		rmiUrlsStr = rmiUrlsStr.deleteCharAt(rmiUrlsStr.length() - 1);
		LOG.debug("last rmiUrls:" + rmiUrlsStr.toString());
		configuration.cacheManagerPeerProviderFactory(new FactoryConfiguration<FactoryConfiguration<?>>()
				.className(RMICacheManagerPeerProviderFactory.class.getName())
				.properties("peerDiscovery=manual,rmiUrls=" + rmiUrlsStr));
	}

	/**
	 * 
	* @Title: configAutomaticCacheManagerPeerProviderFactory
	* @Description: 配置缓存管理自动注册提供者工厂
	* @return void    返回类型
	* @param configuration
	 */
	private void configAutomaticCacheManagerPeerProviderFactory(Configuration configuration) {
		configuration.cacheManagerPeerProviderFactory(new FactoryConfiguration<FactoryConfiguration<?>>()
				.className(RMICacheManagerPeerProviderFactory.class.getName())
				.properties("peerDiscovery=automatic,multicastGroupAddress="
						+ ehClusterConfig.getMulticastGroupAddress().trim() + ",multicastGroupPort="
						+ ehClusterConfig.getMulticastGroupPort() + ",timeToLive=32"));
	}
	
	/**
	 * 
	* @Title: configCacheManagerPeerListenerFactory
	* @Description:配置缓存同步管理监听工厂
	* @return void    返回类型
	* @param configuration
	 */
	private void configCacheManagerPeerListenerFactory(Configuration configuration) {
		configuration.cacheManagerPeerListenerFactory(new FactoryConfiguration<FactoryConfiguration<?>>()
				.className(RMICacheManagerPeerListenerFactory.class.getName())
				.properties("hostName=" + ehClusterConfig.getHostName().trim() + ",port="
						+ ehClusterConfig.getPort() + ",socketTimeoutMillis=2000"));
	}
	
	/**
	 * 
	* @Title: configCacheEventListenerFactory
	* @Description: 配置缓存监听工厂
	* @return void    返回类型
	* @param configuration
	 */
	private void configCacheEventListenerFactory(Configuration configuration) {
		Properties defaultRMICacheReplicatorFactoryProperties = PropertyUtil
				.parseProperties(defaultRMICacheReplicatorFactoryPropertiesName, ",");
		Map<String, CacheConfiguration> cacheConfigurations = configuration.getCacheConfigurations();
		
		Set<String> filterNoSyncCache = filterNoSyncCache(configuration.getCacheConfigurationsKeySet());
		
		for (String key : filterNoSyncCache) {
			
			CacheConfiguration cacheConfiguration = cacheConfigurations.get(key);

			boolean hasRMICacheReplicatorFactory = false;
			@SuppressWarnings("unchecked")
			List<CacheEventListenerFactoryConfiguration> cacheEventListenerConfigurations = cacheConfiguration
					.getCacheEventListenerConfigurations();
			//已经配置缓存监听情况
			if (cacheEventListenerConfigurations != null && !cacheEventListenerConfigurations.isEmpty()) {
				for (CacheEventListenerFactoryConfiguration c1 : cacheEventListenerConfigurations) {
					String className = c1.getFullyQualifiedClassPath();
					if (className.equals(RMICacheReplicatorFactory.class.getName())) {
						hasRMICacheReplicatorFactory = true;
						Properties parseProperties = PropertyUtil.parseProperties(c1.getProperties(),
								c1.getPropertySeparator());
						//属性为空 设置默认
						if (parseProperties == null) {
							c1.properties(defaultRMICacheReplicatorFactoryPropertiesName);
							continue;
						}
						//属性不为空 ,和默认属性合并
						Enumeration<?> propertyNames = parseProperties.propertyNames();
						while (propertyNames.hasMoreElements()) {
							String key1 = (String) propertyNames.nextElement();
							String property = parseProperties.getProperty(key1);
							if (StringUtils.hasText(property)) {
								defaultRMICacheReplicatorFactoryProperties.put(key1, property);
							}
						}
						//重新设置合并后的属性
						Enumeration<?> propertyNames1 = defaultRMICacheReplicatorFactoryProperties.propertyNames();
						StringBuilder sb = new StringBuilder();
						while (propertyNames1.hasMoreElements()) {
							String key1 = (String) propertyNames1.nextElement();
							String property = defaultRMICacheReplicatorFactoryProperties.getProperty(key1);
							sb.append(key1).append("=").append(property).append(",");
						}
						c1.setProperties(sb.substring(0, sb.length() - 1).toString());
					}
				}
			}
			//未配置缓存监听情况 设置默认RMICacheReplicatorFactory即属性
			if (!hasRMICacheReplicatorFactory) {
				CacheEventListenerFactoryConfiguration cacheEventListenerFactoryConfiguration = new CacheEventListenerFactoryConfiguration();
				cacheEventListenerFactoryConfiguration.className(RMICacheReplicatorFactory.class.getName());
				cacheEventListenerFactoryConfiguration.properties(defaultRMICacheReplicatorFactoryPropertiesName);
				cacheConfiguration.addCacheEventListenerFactory(cacheEventListenerFactoryConfiguration);
			}

		}
	}
	
	/**
	 * 
	* @Title: filterNoSyncCache
	* @Description: 过滤掉不同步的缓存key
	* @returnSet<String>    返回类型 过滤后的缓存配置key
	* @param cacheConfigurationsKeySet
	* @return
	 */
	private Set<String> filterNoSyncCache(Set<String> cacheConfigurationsKeySet){
		Set<String> filteredCacheConfigurationKeySet = new HashSet<String>();
		filteredCacheConfigurationKeySet.addAll(cacheConfigurationsKeySet);
		String unSyncCacheName = ehClusterConfig.getUnSyncCacheName();
		StringTokenizer unSyncCacheNameTokenizer = new StringTokenizer(unSyncCacheName, "|");
		while(unSyncCacheNameTokenizer.hasMoreTokens()){
			String nextToken = unSyncCacheNameTokenizer.nextToken();
			for (String key : cacheConfigurationsKeySet) {
				if(key.equals(nextToken)){
					filteredCacheConfigurationKeySet.remove(nextToken);
					continue;
				}
			}
		}
		return filteredCacheConfigurationKeySet;
	}

	@Override
	public CacheManager getObject() {
		return this.cacheManager;
	}

	@Override
	public Class<? extends CacheManager> getObjectType() {
		return this.cacheManager != null ? this.cacheManager.getClass() : CacheManager.class;
	}

	@Override
	public boolean isSingleton() {
		return true;
	}

	@Override
	public void destroy() {
		if (this.locallyManaged) {
			LOG.info("Shutting down EhCache CacheManager");
			this.cacheManager.shutdown();
		}
	}
	
	public ClusterEhCacheConfig getEhClusterConfig() {
		return ehClusterConfig;
	}

	public void setEhClusterConfig(ClusterEhCacheConfig ehClusterConfig) {
		this.ehClusterConfig = ehClusterConfig;
	}
}

ClusterEhCacheConfig 集群配置,从配置文件获取属性, 默认自动方式

package com.wisedu.zzfw.template.common.cache.cluster;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
* @ClassName: ClusterEhCacheConfig
* @Description: ehcache集群配置
* @author  luanhy
* @date 2017年8月19日 下午3:24:55
* @Copyright: Copyright (c) 2017 wisedu
*/
@Component
public class ClusterEhCacheConfig {
	
	/**
	 * 是否启用集群
	 */
	@Value("${ehcache.enableCluster:true}")
	private boolean enableCluster;
	
	/**
	 * 无需同步的缓存名称 |分隔
	 */
	@Value("${ehcache.unSyncCacheName:noCache|printOrderCache}")
	private String unSyncCacheName;
	
	/**
	 * 缓存提供者rmi通信方式 自动和手动
	 */
	@Value("${ehcache.provider.peerDiscovery:automatic}")
	private String peerDiscovery;
	
	/**
	 * 缓存提供者广播组地址
	 */
	@Value("${ehcache.provider.automatic.multicastGroupAddress:224.1.1.1}")
	private String multicastGroupAddress;
	
	/**
	 * 缓存提供者广播组端口
	 */
	@Value("${ehcache.provider.automatic.multicastGroupPort:41000}")
	private int multicastGroupPort;

	/**
	 * 缓存提供者需要通知的rmi地址
	 */
	@Value("${ehcache.provider.manual.rmiUrls:127.0.0.1:40001}")
	private String rmiUrls;
	
	/**
	 * 缓存监听者端口是否随机 (自动配置时生效)
	 */
	@Value("${ehcache.listener.randomPort:true}")
	private boolean randomPort;
	
	/**
	 * 缓存监听者端口地址
	 */
	@Value("${ehcache.listener.hostName:127.0.0.1}")
	private String hostName;
	
	/**
	 * 缓存监听者默认端口
	 */
	@Value("${ehcache.listener.port:40002}")
	private int port;
	
	@PostConstruct
	public void init(){
		if(randomPort){
			// 41000 默认广播随机端口号 不能随机
			PortUtil p = new PortUtil();
			// 40000 -40999监听随机端口号
			port = p.getUnAvailableRandomPort(40000, 40999);
		}
	}

	public String getPeerDiscovery() {
		return peerDiscovery;
	}

	public void setPeerDiscovery(String peerDiscovery) {
		this.peerDiscovery = peerDiscovery;
	}

	public String getMulticastGroupAddress() {
		return multicastGroupAddress;
	}

	public void setMulticastGroupAddress(String multicastGroupAddress) {
		this.multicastGroupAddress = multicastGroupAddress;
	}

	public int getMulticastGroupPort() {
		return multicastGroupPort;
	}

	public void setMulticastGroupPort(int multicastGroupPort) {
		this.multicastGroupPort = multicastGroupPort;
	}

	public String getRmiUrls() {
		return rmiUrls;
	}

	public void setRmiUrls(String rmiUrls) {
		this.rmiUrls = rmiUrls;
	}

	public String getHostName() {
		return hostName;
	}

	public void setHostName(String hostName) {
		this.hostName = hostName;
	}

	public int getPort() {
		return port;
	}

	public void setPort(int port) {
		this.port = port;
	}
	
	public boolean getRandomPort() {
		return randomPort;
	}

	public void setRandomPort(boolean randomPort) {
		this.randomPort = randomPort;
	}
	
	public boolean getEnableCluster() {
		return enableCluster;
	}

	public void setEnableCluster(boolean enableCluster) {
		this.enableCluster = enableCluster;
	}
	
	public String getUnSyncCacheName() {
		return unSyncCacheName;
	}

	public void setUnSyncCacheName(String unSyncCacheName) {
		this.unSyncCacheName = unSyncCacheName;
	}

	@Override
	public String toString() {
		return "EhClusterConfig [enableCluster=" + enableCluster + ", unSyncCacheName=" + unSyncCacheName + ",randomPort=" + randomPort + ", peerDiscovery="
				+ peerDiscovery + ", multicastGroupAddress=" + multicastGroupAddress + ", multicastGroupPort="
				+ multicastGroupPort + ", rmiUrls=" + rmiUrls + ", hostName=" + hostName + ", port=" + port + "]";
	}

	
}

工具类PortUtil  自动模式下获取空闲端口

package com.wisedu.zzfw.template.common.cache.cluster;

import java.util.Random;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* @ClassName: PortUtil
* @Description: 端口工具类
* @author  luanhy
* @date 2017年8月19日 下午3:30:18
* @Copyright: Copyright (c) 2017 wisedu
*/
public class PortUtil {
	
	private static final Logger LOG = LoggerFactory.getLogger(PortUtil.class);

	private int maxRandomCount = 1000;
	
	private int randomCount = 0;
	
	public void resetCount(){
		randomCount = 0;
	}

	/**
	 * 
	 * @Title: isPortAvailable
	 * @Description: 端口是否未被用
	 * @param port
	 * @return
	 */
	public boolean isPortAvailable(int port) {
		try {
			
			String[] commond = new String[2];
			commond[0] = "netstat";
			String encoding ="gbk";
			SystemCommandUtil systemCommandUtil = new SystemCommandUtil();
			if (systemCommandUtil.isWindows()) {
				commond[1] = "-aon";
			} else {
				commond[1] = "-anp";
				encoding = "utf-8";
			}
			String ret = systemCommandUtil.excuteCmdMultiThread(commond, encoding);
			boolean matches = Pattern.compile("(.+)("+port+"\\s+)(.*)").matcher(ret).find();
			return !matches;
		} catch (Exception e) {
			LOG.error("",e);
			return false;
		}
	}

	/**
	 * 
	 * @Title: getRandomPort
	 * @Description: 获取随机端口号
	 * @param minPort
	 * @param maxPort
	 * @return
	 */
	private int getRandomPort(int minPort, int maxPort) {
		Random random = new Random();
		int s = random.nextInt(maxPort) % (maxPort - minPort + 1) + minPort;
		return s;
	}

	/**
	 * 
	 * @Title: getUnAvailablePort
	 * @Description:获取未被占用的随机端口号
	 * @param minPort
	 * @param maxPort
	 * @return
	 */
	public int getUnAvailableRandomPort(int minPort, int maxPort) {
		if ((++randomCount) > maxRandomCount) {
			throw new RuntimeException("无法从" + minPort + "到" + maxPort + "绑定ehcache rmi同步端口号,请检查端口占用情况");
		}
		int randomPort = getRandomPort(minPort, maxPort);
		if (!isPortAvailable(randomPort)) {
			return getUnAvailableRandomPort(minPort, maxPort);
		}
		return randomPort;
	}

}

工具类 用于调用命令行获取可用端口

package com.wisedu.zzfw.template.common.cache.cluster;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

/**
 * @ClassName: SystemCommandUtil
 * @Description: 命令工具
 * @author luanhy
 * @date 2017年8月19日 下午3:32:27
 * @Copyright: Copyright (c) 2017 wisedu
 */
public class SystemCommandUtil {

	private static final Logger LOG = LoggerFactory.getLogger(SystemCommandUtil.class);

	public boolean isWindows() {
		String osName = System.getProperty("os.name");
		return osName.indexOf("Windows") != -1;
	}

	/** 执行外部程序,并获取标准输出 */
	public String excuteCmdMultiThread(String[] cmd, String encoding) {
		Process p = null;
		try {
			p = Runtime.getRuntime().exec(cmd);
			/* 为"错误输出流"单独开一个线程读取之,否则会造成标准输出流的阻塞 */
			Thread t = new Thread(new InputStreamRunnable(p.getErrorStream(), "ErrorStream"));
			t.start();
			/* "标准输出流"就在当前方法中读取 */
			String encodingStr = StringUtils.isEmpty(encoding) ? "gbk" : encoding;
			String string = IOUtils.toString(p.getInputStream(), encodingStr);
			return string;
		} catch (Exception e) {
			LOG.error("执行外部程序,并获取标准输出异常", e);
		} finally {
			p.destroy();
		}
		return null;
	}

	/** 读取InputStream的线程 */
	class InputStreamRunnable implements Runnable {
		BufferedReader bReader = null;
		String type = null;

		public InputStreamRunnable(InputStream is, String typeStr) {
			try {
				bReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(is), "UTF-8"));
				type = typeStr;
			} catch (Exception ex) {
				LOG.error("读取InputStream的线程异常", ex);
			}
		}

		public void run() {
			String line = null;
			try {
				while ((line = bReader.readLine()) != null) {
					LOG.error(line);
				}
				bReader.close();
			} catch (Exception ex) {
				LOG.error("", ex);
			}
		}
	}
}

猜你喜欢

转载自blog.csdn.net/v2sking/article/details/78995310