以下内容,个人总结, 只是项目中用到, 并且知识范围内做的修改, 不妥之处请打脸. 脸放这()
项目中使用的Ehcache做的缓存处理, 但是ehcache集群时, 判断是否能正常通讯只通过操作缓存后, 查询其他机器缓存是否正常清除或者添加有点黑盒的感觉, 看不见具体的缓存添加移除过程.
另外由于@Cacheable不支持的key不支持正则表达式, 有些缓存时单项缓存,但是修改时 ,通过@CacheEvict来清除不知道具体的key, 只知道key的前半部分或者后半部分, ehcache的key又不支持正则, 无法模糊清除.
针对上面的两个情况, 对ehache的源码部分做了一丁点的修改, 支持正则表达式, 并且集群环境下能打印一下缓存添加移除的日志.
对于集群时, 集群机器间的地址查找, 可以通过增加log4j的debug日志看见, 但是仅是机器查找的日志 lookup ipxxx....的日志字样.
log4j.logger.net.sf.ehcache.distribution.RMICacheManagerPeerListener=DEBUG,Console
缓存变化异步通知类是:
log4j.logger.net.sf.ehcache.distribution.RMIAsynchronousCacheReplicator=DEBUG,Console
真正处理接收到缓存变化异步通知处理类是:
log4j.logger.net.sf.ehcache.distribution.RMICachePeer=DEBUG,Console
RMICachePeer是缓存处理的最终类, 正则表达式的支持也是放到这个类上做的处理.
/**
* Copyright Terracotta, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.sf.ehcache.distribution;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import java.io.Serializable;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;
import java.util.List;
import net.sf.ehcache.distribution.RmiEventMessage.RmiEventType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.thinkgem.jeesite.common.utils.Collections3;
/**
* An RMI based implementation of CachePeer
.
*
* This class features a customised RMIClientSocketFactory which enables socket timeouts to be configured.
*
* @author Greg Luck
* @version $Id: RMICachePeer.java 5631 2012-05-10 08:31:33Z teck $
*/
//remove方法增加正则表达式支持 mod by tom
public class RMICachePeer extends UnicastRemoteObject implements CachePeer, Remote {
private static final Logger LOG = LoggerFactory.getLogger(RMICachePeer.class.getName());
private final String hostname;
private final Integer rmiRegistryPort;
private Integer remoteObjectPort;
private final Ehcache cache;
/**
* Construct a new remote peer.
*
* @param cache The cache attached to the peer
* @param hostName The host name the peer is running on.
* @param rmiRegistryPort The port number on which the RMI Registry listens. Should be an unused port in
* the range 1025 - 65536
* @param remoteObjectPort the port number on which the remote objects bound in the registry receive calls.
* This defaults to a free port if not specified.
* Should be an unused port in the range 1025 - 65536
* @param socketTimeoutMillis
* @throws RemoteException
*/
public RMICachePeer(Ehcache cache, String hostName, Integer rmiRegistryPort, Integer remoteObjectPort,
Integer socketTimeoutMillis)
throws RemoteException {
super(remoteObjectPort.intValue(), new ConfigurableRMIClientSocketFactory(socketTimeoutMillis),
ConfigurableRMIClientSocketFactory.getConfiguredRMISocketFactory());
this.remoteObjectPort = remoteObjectPort;
this.hostname = hostName;
this.rmiRegistryPort = rmiRegistryPort;
this.cache = cache;
}
/**
* {@inheritDoc}
*
* This implementation gives an URL which has meaning to the RMI remoting system.
*
* @return the URL, without the scheme, as a string e.g. //hostname:port/cacheName
*/
public final String getUrl() {
return new StringBuilder()
.append("//")
.append(hostname)
.append(":")
.append(rmiRegistryPort)
.append("/")
.append(cache.getName())
.toString();
}
/**
* {@inheritDoc}
*
* This implementation gives an URL which has meaning to the RMI remoting system.
*
* @return the URL, without the scheme, as a string e.g. //hostname:port
*/
public final String getUrlBase() {
return new StringBuilder()
.append("//")
.append(hostname)
.append(":")
.append(rmiRegistryPort)
.toString();
}
/**
* Returns a list of all elements in the cache, whether or not they are expired.
*
* The returned keys are unique and can be considered a set.
*
* The List returned is not live. It is a copy.
*
* The time taken is O(n). On a single cpu 1.8Ghz P4, approximately 8ms is required
* for each 1000 entries.
*
* @return a list of {@link Object} keys
*/
public List getKeys() throws RemoteException {
List keys = cache.getKeys();
if (keys instanceof Serializable) {
return keys;
}
return new ArrayList(keys);
}
/**
* Gets an element from the cache, without updating Element statistics. Cache statistics are
* still updated.
*
* @param key a serializable value
* @return the element, or null, if it does not exist.
*/
public Element getQuiet(Serializable key) throws RemoteException {
return cache.getQuiet(key);
}
/**
* Gets a list of elements from the cache, for a list of keys, without updating Element statistics. Time to
* idle lifetimes are therefore not affected.
*
* Cache statistics are still updated.
*
* Callers should ideally first call this method with a small list of keys to gauge the size of a typical Element.
* Then a calculation can be made of the right number to request each time so as to optimise network performance and
* not cause an OutOfMemory error on this Cache.
*
* @param keys a list of serializable values which represent keys
* @return a list of Elements. If an element was not found or null, it will not be in the list.
*/
public List getElements(List keys) throws RemoteException {
if (keys == null) {
return new ArrayList();
}
List elements = new ArrayList();
for (int i = 0; i < keys.size(); i++) {
Serializable key = (Serializable) keys.get(i);
Element element = cache.getQuiet(key);
if (element != null) {
elements.add(element);
}
}
return elements;
}
/**
* Puts an Element into the underlying cache without notifying listeners or updating statistics.
*
* @param element
* @throws java.rmi.RemoteException
* @throws IllegalArgumentException
* @throws IllegalStateException
*/
public void put(Element element) throws RemoteException, IllegalArgumentException, IllegalStateException {
cache.put(element, true);
if (LOG.isDebugEnabled()) {
// LOG.debug("RMICachePeer for cache " + cache.getName() + ": remote put received. Element is: " + element);
LOG.debug("RMICachePeer for cache " + cache.getName() + ": remote put received. Element is: " + element.getObjectKey());
}
}
/**
* Removes an Element from the underlying cache without notifying listeners or updating statistics.
*
* @param key
* @return true if the element was removed, false if it was not found in the cache
* @throws RemoteException
* @throws IllegalStateException
*/
public boolean remove(Serializable key) throws RemoteException, IllegalStateException {
if (LOG.isDebugEnabled()) {
LOG.debug("RMICachePeer for cache " + cache.getName() + ": remote remove received for key: " + key);
}
//add by tom 2017年11月8日 ,移除符合自定义正则表达式的缓存
removeRegex(key);
return cache.remove(key, true);
}
/**
* remove regex pattern cache key. add by tom
* @param key
*/
private void removeRegex(Serializable key) {
try {
String strKey = key.toString();
if (strKey.matches("<\\[.+\\]>")) {
String regex = strKey.substring(2,strKey.length()-2);
LOG.debug("RMICachePeer for cache " + cache.getName() + ": remote regex: " + regex);
@SuppressWarnings("unchecked")
List kList = cache.getKeys();
if (kList == null || kList.isEmpty()) {
LOG.debug("keys is empty for cache " + cache.getName() + ": remote regex: " + regex);
return;
}
LOG.debug("cache " + cache.getName() + " exists keys: " + kList.size());
for (String k : kList) {
// LOG.debug("RMICachePeer for cache " + cache.getName() + ": remote remove regex key : " + k);
if (k.matches(regex)) {
if (LOG.isDebugEnabled()) {
LOG.debug("RMICachePeer for cache " + cache.getName() + ": remote remove regex matcher key: " + k);
}
cache.remove(k, true);
}
}
}
} catch (IllegalStateException e) {
LOG.error(e.getMessage(), e);
} catch (CacheException e) {
LOG.error(e.getMessage(), e);
}
}
/**
* Removes all cached items.
*
* @throws IllegalStateException if the cache is not {@link net.sf.ehcache.Status#STATUS_ALIVE}
*/
public void removeAll() throws RemoteException, IllegalStateException {
if (LOG.isDebugEnabled()) {
LOG.debug("RMICachePeer for cache " + cache.getName() + ": remote removeAll received");
}
cache.removeAll(true);
}
/**
* Send the cache peer with an ordered list of {@link EventMessage}s
*
* This enables multiple messages to be delivered in one network invocation.
*/
public void send(List eventMessages) throws RemoteException {
for (int i = 0; i < eventMessages.size(); i++) {
RmiEventMessage eventMessage = (RmiEventMessage) eventMessages.get(i);
if (eventMessage.getType() == RmiEventType.PUT) {
put(eventMessage.getElement());
} else if (eventMessage.getType() == RmiEventType.REMOVE) {
remove(eventMessage.getSerializableKey());
} else if (eventMessage.getType() == RmiEventType.REMOVE_ALL) {
removeAll();
} else {
LOG.error("Unknown event: " + eventMessage);
}
}
}
/**
* Gets the cache name
*/
public final String getName() throws RemoteException {
return cache.getName();
}
/**
* {@inheritDoc}
*/
public final String getGuid() throws RemoteException {
return cache.getGuid();
}
/**
* Gets the cache instance that this listener is bound to
*/
final Ehcache getBoundCacheInstance() {
return cache;
}
/**
* Returns a String that represents the value of this object.
*/
public String toString() {
StringBuilder buffer = new StringBuilder("URL: ");
buffer.append(getUrl());
buffer.append(" Remote Object Port: ");
buffer.append(remoteObjectPort);
return buffer.toString();
}
}
上面的类增加了一个方法:
removeRegex
在使用@CacheEvict时, key值如果作为正则表达式, 需要 key = '<[正则表达式]>', 用<[]>包含的内容作为正则表达式处理, 通过将ehcache中的缓存key取出并做正则匹配, 匹配上时, 调用cache.remove方法将缓存移除.