采用JGroups主要是实现了ehcached之间的缓存数据共享
1、引入GAV坐标 webapp-->pom.xml
`<!-- https://mvnrepository.com/artifact/net.sf.ehcache/ehcache-jgroupsreplication -->
<!-- ehcache 做集群管理 -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-jgroupsreplication</artifactId>
<version>1.5</version>
</dependency>`
2、修改Ehcache配置
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="java.io.tmpdir/localcache/egrantapp"/>
<defaultCache maxElementsInMemory="1000" eternal="false"
timeToIdleSeconds="1200" timeToLiveSeconds="1200" overflowToDisk="true"
maxElementsOnDisk="10000" diskPersistent="false"
diskExpiryThreadIntervalSeconds="1200" memoryStoreEvictionPolicy="LRU">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.jgroups.JGroupsCacheReplicatorFactory"
properties="replicateAsynchronously=true, replicatePuts=true,
replicateUpdates=true, replicateUpdatesViaCopy=false, replicateRemovals=true" />
</defaultCache>
<!-- 具体参数详解 https://blog.csdn.net/u010615514/article/details/9820593 多节点参考
initial_hosts -->
<cacheManagerPeerProviderFactory
class="com.iris.egrant.jgroups.CustomJGroupsCacheManagerPeerProviderFactory"
properties="connect=
TCP(bind_addr=%1$s;bind_port=%2$s;send_buf_size=1280000;max_bundle_size=1280000;):
TCPPING(initial_hosts=%3$s;port_range=1;timeout=5000;num_initial_members=2):
VERIFY_SUSPECT(timeout=1500):
pbcast.NAKACK(retransmit_timeout=100,200,300,600,1200,2400,4800;discard_delivered_msgs=true):
pbcast.GMS(print_local_addr=true;join_timeout=5000):
MERGE2(min_interval=3000;max_interval=5000):
FD_ALL(interval=5000;timeout=20000):
FD(timeout=5000;max_tries=48;):
pbcast.STABLE(stability_delay=1000;desired_avg_gossip=20000;max_bytes=0)"
propertySeparator="::"/>
<!--notice: eternal:true表示对象永不过期,此时会忽略timeToIdleSeconds和timeToLiveSeconds属性 -->
<!-- service application cache -->
<!-- loginIPCache -->
<cache name="loginIPCache" maxElementsInMemory="1000"
eternal="false" overflowToDisk="true" timeToIdleSeconds="600"
timeToLiveSeconds="600" diskPersistent="true" maxElementsOnDisk="50000">
<cacheEventListenerFactory
class="net.sf.ehcache.distribution.jgroups.JGroupsCacheReplicatorFactory"
properties="replicateAsynchronously=true, replicatePuts=true,
replicateUpdates=true, replicateUpdatesViaCopy=false, replicateRemovals=true" />
</cache>
3、重写读取配置源码JGroupsCacheManagerPeerProviderFactory
public class CustomJGroupsCacheManagerPeerProviderFactory extends JGroupsCacheManagerPeerProviderFactory {
private static final Logger LOG = LoggerFactory
.getLogger(CustomJGroupsCacheManagerPeerProviderFactory.class.getName());
private static final String CHANNEL_NAME = "channelName";
private static final String CONNECT = "connect";
private static final String FILE = "file";
@Override
public CacheManagerPeerProvider createCachePeerProvider(CacheManager cacheManager, Properties properties) {
LOG.trace("Creating JGroups CacheManagerPeerProvider for {} with properties:\n{}", cacheManager.getName(),
properties);
final String connect = this.getProperty(CONNECT, properties);
final String file = this.getProperty(FILE, properties);
final String channelName = this.getProperty(CHANNEL_NAME, properties);
final JGroupsCacheManagerPeerProvider peerProvider;
if (file != null) {
if (connect != null) {
LOG.warn("Both '" + CONNECT + "' and '" + FILE + "' properties set. '" + CONNECT + "' will be ignored");
}
final ClassLoader contextClassLoader = ClassLoaderUtil.getStandardClassLoader();
final URL configUrl = contextClassLoader.getResource(file);
LOG.debug("Creating JGroups CacheManagerPeerProvider for {} with configuration file: {}",
cacheManager.getName(), configUrl);
peerProvider = new JGroupsCacheManagerPeerProvider(cacheManager, configUrl);
} else {
LOG.debug("Creating JGroups CacheManagerPeerProvider for {} with configuration:\n{}",
cacheManager.getName(), connect);
peerProvider = new JGroupsCacheManagerPeerProvider(cacheManager, connect);
}
peerProvider.setChannelName(channelName);
return peerProvider;
}
private String getProperty(final String name, Properties properties) {
String property = this.extractAndLogProperty(name, properties);
if (property != null) {
property = property.trim();
property = property.replaceAll(" ", "");
if (property.equals("")) {
property = null;
}
}
return property;
}
private String extractAndLogProperty(String name, Properties properties) {
if (properties == null || properties.size() == 0) {
return null;
}
String foundValue = (String) properties.get(name);
if (foundValue != null) {
foundValue = foundValue.trim();
}
if (CONNECT.equals(name)) {
String bindPort = System.getProperty("bind_port", "9988");
String bind_addr = System.getProperty("bind_addr", "localhost");
String initialHosts = System.getProperty("initial_hosts", "localhost[9988]");
foundValue = String.format(foundValue, bind_addr,bindPort, initialHosts);
}
if (LOG.isDebugEnabled()) {
LOG.debug(new StringBuilder().append("Value found for ").append(name).append(": ").append(foundValue)
.toString());
}
return foundValue;
}
}
4、tomcat启动参数添加
-Dbind_addr=192.168.10.229
-Dbind_port=9113
-Dinitial_hosts=192.168.10.229[9113],192.168.10.229[9114]
-Djava.net.preferIPv4Stack=true
5、注意事项
3.最低支持集群节点数num_initial_members=2,少于两个节点则报问题8 4.发送缓冲区大小send_buf_size,发送前要排队的最大字节数max_bundle_size 5.源码中JGroups-2.10.xsd 配置参数说明,不需要加到项目中去 6.replicateAsynchronously true|false 复制操作是异步(ture),还是同步(false) 设置为false 7.jgroups涉及到消息为实体的情况,在接收消息时会报错ClassNotFound,实体反序列化不了,修改方法参考BaseCacheService的put get方法。
/**
* 存储缓存.
*
* @param key
* @param value
*/
private void putCacheData(Object key, Object value) {
Assert.notNull(key, "key参数不能为空");
/*Element element = new Element(key, value);
getCache().put(element);*/
Map<String, String> map = new HashMap<String, String>();
map.put("className",value.getClass().getName());
map.put("value", JacksonUtils.jsonObjectSerializer(value));
Element element = new Element(key, JacksonUtils.jsonMapSerializer(map));
getCache().put(element);
}
* 获取缓存数据.
*
* @param key
* @return
*/
private Object getCacheData(Object key) {
Assert.notNull(key, "key参数不能为空");
Element element = null;
logger.info("key:{}",key);
try {
element = getCache().get(key);
if (element != null) {
logger.debug("Cache get: " + (element != null) + "; key: " + key);
try {
Map<String, String> map = JacksonUtils.jsonToMap(element.getValue().toString());
Class className = Class.forName(map.get("className"));
return JacksonUtils.jsonObject(map.get("value"),className);
}catch (Exception e){
e.printStackTrace();
}
}
logger.info("element is null !!!!!");
return null;
} catch (IllegalStateException e) {
logger.error("读取cache,key=" + key, e);
} catch (CacheException e) {
logger.error("读取cache,key=" + key, e);
}
return null;
}
8.集群时不成功,报错提示为dropped message from xxx(not in ... ,使用netstat -apn|grep 端口,查看ip是否正确,ip正确则切换配置端口试试。