Ehcache在集群时, 测试是否集群间能正常通讯

          以下内容,个人总结, 只是项目中用到, 并且知识范围内做的修改, 不妥之处请打脸.    脸放这()

        项目中使用的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方法将缓存移除.


        



你可能感兴趣的:(Spring)