其他分享
首页 > 其他分享> > Spring-ehcache RMI形式的分布式缓存配置

Spring-ehcache RMI形式的分布式缓存配置

作者:互联网

Spring-ehcache RMI形式的分布式缓存配置

简介

这是本人因为工作需要研究的关于ehcache的分布式RMI模式的使用心得已经自己的一些心得。

git 源码

git 样例代码

详细介绍

话不多说,下面结合demo分步做详细的介绍

jar依赖

<dependency>
   <groupId>net.sf.ehcache</groupId>
   <artifactId>ehcache-core</artifactId>
   <version>2.6.6</version>
</dependency>
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context-support</artifactId>
   <version>4.2.7.RELEASE</version>
</dependency>

spring.xml文件中引用spring-ehcache.xml

注解使用cacheManager

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
			http://www.springframework.org/schema/beans/spring-beans.xsd
			http://www.springframework.org/schema/cache
       	http://www.springframework.org/schema/cache/spring-cache.xsd">
    
    <!-- ehcache config -->
    <cache:annotation-driven  cache-manager="ehCacheCacheManager"/>
    <bean id="ehCacheCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" p:cache-manager-ref="cacheManagerFactoryBean"/>
    <!-- EhCache library setup -->
    <bean id="cacheManagerFactoryBean" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:config-location="classpath:spring-ehcache.xml" p:shared="true"/>

</beans>

spring-ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="false"
    monitoring="autodetect" dynamicConfig="true">

ehcache标签的属性

cacheManagerPeerProviderFactory

它分布式缓存管理器提供者,指定一个CacheManagerPeerProviderFactory,它将用于创建一个CacheManagerPeerProvider, CacheManagerPeerProvider侦测集群中的其它事务管理器,实现和分布式环境下的缓存同步。

相关属性介绍:

RMI 手动配置

<!-- 手动配置rmi同步的地址信息(这种模式本人亲测多服务器试验还有问题)-->
<cacheManagerPeerProviderFactory
        class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
              properties="peerDiscovery=manual, 
              rmiUrls=//127.0.0.1:40002/testCache|//127.0.0.1:40002/testCache2 "
        propertySeparator="," />

RMI 自动广播

这样当缓存改变时,ehcache会向230.0.0.1端口4446发RMI UDP组播包

<!-- 自动广播式rmi形式(这种模式本人目前多服务器试验还有问题) --> 
<cacheManagerPeerProviderFactory 
  class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
	    properties="peerDiscovery=automatic, 
	               multicastGroupAddress=230.0.0.1,
	               multicastGroupPort=4446, 
	               timeToLive=32"/>

cacheManagerPeerListenerFactory

每个CacheManagerPeerListener监听从成员们发向当前CacheManager的消息。配置 CacheManagerPeerListener需要指定一个CacheManagerPeerListenerFactory,它以插件的机制实现, 用来创建CacheManagerPeerListener。
  Ehcache有一个内置的基于RMI的分布系统。它的监听器是RMICacheManagerPeerListener,这个监听器可以用RMICacheManagerPeerListenerFactory来配置

<!-- 本机缓存的信息对应的地址和端口配置监听器的工厂类 -->
      <cacheManagerPeerListenerFactory 
         class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"
          properties="hostName=127.0.0.1, port=40002,socketTimeoutMillis=2000" /> 

具体cache 对象配置

<!-- 缓存最长存在10分钟后失效,如果5分钟未访问,缓存也会失效 -->
<cache name="testCache"
           maxEntriesLocalHeap="10000" 
           eternal="false"
           timeToIdleSeconds="300" 
           timeToLiveSeconds="600"> 
        <cacheEventListenerFactory
            class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
            properties="replicateAsynchronously=false, replicatePuts=true,
                            replicatePutsViaCopy=true, replicateUpdates=true,
                            replicateUpdatesViaCopy=true, replicateRemovals=true" />
    </cache>
    
     <!-- 缓存最长存在99天后失效,如果99天未访问,缓存也会失效 -->
    <cache name="testCache2" maxEntriesLocalHeap="10000" eternal="false"
        timeToIdleSeconds="8640000" timeToLiveSeconds="8640000">
        <cacheEventListenerFactory
            class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"
            properties="replicateAsynchronously=false, replicatePuts=true,
                            replicatePutsViaCopy=true, replicateUpdates=true,
                            replicateUpdatesViaCopy=true, replicateRemovals=true" />
    </cache>
</ehcache>

cache属性介绍

cacheEventListenerFactory

注册相应的的缓存监听类,用于处理缓存事件,如put,remove,update,和expire bootstrap CacheLoaderFactory:指定相应的BootstrapCacheLoader,用于在初始化缓存,以及自动设置。

bootstrapCacheLoaderFactory

指定相应的BootstrapCacheLoader,用于在初始化缓存,以及自动设置

代码中缓存标记的使用

读取/生成缓存@Cacheable

能够根据方法的请求参数对其结果进行缓存。即当重复使用相同参数调用方法的时候,方法本身不会被调用执行,即方法本身被略过了,取而代之的是方法的结果直接从缓存中找到并返回了。

  • 缓存key还可以用如下规则组成,当我们要使用root作为key时,可以不用写root直接@Cache(key=“caches[1].name”)。因为他默认是使用#root的
    1.methodName 当前方法名 #root.methodName
    2.method 当前方法 #root.method.name
    3.target 当前被动用对象 #root.target
    4.targetClass 当前被调用对象 Class#root.targetClass
    5.args 当前方法参数组成的数组 #root.args[0]
    6.caches 当前被调用方法所使用的Cache #root.caches[0],name
    7.方法参数 假设包含String型参数str #str
    #p0代表方法的第一个参数
    假设包含HttpServletRequest型参数request #request.getAttribute(‘usId32’) 调用入参对象的相关包含参数的方法
    假设包含User型参数user #user.usId 调用入参对象的无参方法可以直接用此形式
    8.字符串 ‘字符串内容’
    /**
     * 生成缓存,同时下一次在调用此方法优先从缓存中获取信息
     * 读取/生成缓存@Cacheable
     * 能够根据方法的请求参数对其结果进行缓存。即当重复使用相同参数调用方法的时候,方法本身不会被调用执行,即方法本身被略过了,取而代之的是方法的结果直接从缓存中找到并返回了。
     * @param hosId
     * @param request
     * @return
     */
    @Cacheable(value="testCache",key="#hosId+'_'+'createTestCacheSuccess'", condition="#hosId!=null",unless="#result.result!=true or #result.data==null")
    @RequestMapping(value = "/{hosId}/createTestCacheSuccess", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
    @ResponseBody
    public ResultVo createTestCacheSuccess(@PathVariable Long hosId, HttpServletRequest request) {

        ResultVo resultVo = new ResultVo();
        resultVo.setKind(SUCCESS_CODE);
        resultVo.setResult(true);
        resultVo.setData((Object)("createTestCacheSuccess成功生成缓存"+System.currentTimeMillis()));
        log.debug("进入实际生成缓存方法体,本次请求未使用缓存,本方法可以生成有效缓存,缓存未失效之前调用该方法将不会进入到方法体");
        return resultVo;
    }

删除缓存@CacheEvict

根据value 和key值来唯一找到缓存记录,并且清理缓存信息

   /**
     * 删除缓存
     * 删除缓存@CacheEvict
     * 根据value 和key值来唯一找到缓存记录,并且清理缓存信息
     * @param hosId
     * @param request
     * @return
     */
    @CacheEvict(value="testCache",key="#hosId+'_'+'deleteCreateTestCacheSuccess'", condition="#hosId!=null")
    @RequestMapping(value = "/{hosId}/deleteCreateTestCacheSuccess", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
    @ResponseBody
    public ResultVo deleteCreateTestCacheSuccess(@PathVariable Long hosId, HttpServletRequest request) {
        log.debug("删除createTestCacheSuccess生成的缓存");
        ResultVo resultVo = new ResultVo();
        resultVo.setKind(SUCCESS_CODE);
        resultVo.setResult(true);
        resultVo.setData((Object)("删除缓存成功"+System.currentTimeMillis()));
        return resultVo;
    }

更新缓存@CachePut

它虽然也可以声明一个方法支持缓存,但它执行方法前是不会去检查缓存中是否存在之前执行过的结果,而是每次都执行该方法,并将执行结果放入指定缓存中。

 /**
     * 生成缓存,同时下一次在调用此方法还是会执行该方法并且同时更新缓存内容
     *
     * 更新缓存@CachePut
     * 它虽然也可以声明一个方法支持缓存,但它执行方法前是不会去检查缓存中是否存在之前执行过的结果,而是每次都执行该方法,并将执行结果放入指定缓存中。
     * @param hosId
     * @param request
     * @return
     */
    @CachePut(value="testCache",key="#hosId+'_'+'updateTestCacheSuccess'", condition="#hosId!=null",unless="#result.result!=true or #result.data==null")
    @RequestMapping(value = "/{hosId}/updateTestCacheSuccess", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
    @ResponseBody
    public ResultVo updateTestCacheSuccess(@PathVariable Long hosId, HttpServletRequest request) {
        log.debug("进入实际生成缓存方法体,本方法可以生成有效缓存,下一次调用该方法依然会进入到方法体");
        ResultVo resultVo = new ResultVo();
        resultVo.setKind(SUCCESS_CODE);
        resultVo.setResult(true);
        resultVo.setData((Object)("updateTestCacheSuccess成功生成缓存"+System.currentTimeMillis()));
        return resultVo;
    }

通过EhCacheCacheManager获取缓存详情

EhCacheCacheManager (管理CacheManager的工具类)是在上面spring.xml 中配置的缓存管理对象

   @Resource
   EhCacheCacheManager ehCacheCacheManager;
   
    /**
     * 从cache 中获取实际缓存信息
     * @param cacheName
     * @param cacheKey
     * @return
     */
    @RequestMapping(value = "getResult", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
    @ResponseBody
    public ResultVo getResult(String cacheName,String cacheKey){
        ResultVo resultVo = null;
        CacheManager cacheManager=ehCacheCacheManager.getCacheManager();
        if (cacheManager!=null){
            Ehcache ehcache = cacheManager.getEhcache(CACHE_NEMA_TESTCACHE);
            if(ehcache!=null){
                Element element = ehcache.get(cacheKey);
                if(element!=null && element.getObjectValue()!=null
                        && element.getObjectValue() instanceof ResultVo){
                    resultVo = (ResultVo)element.getObjectValue();

                }
            }
        }
        return resultVo;
    }

简单信息统计

    /**
     * 从cache 中获取缓存简单的监控信息(数据量少的时候适合这么干,数据量大的时候需要注意性能问题,一下子遍历所有缓存元素这将是一个灾难)
     * @return
     */
    @RequestMapping(value = "getCacheStatistic", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
    @ResponseBody
    public ResultVo getCacheStatistic(){
        ResultVo resultVo = new ResultVo();
        resultVo.setResult(false);
        CacheManager cacheManager=ehCacheCacheManager.getCacheManager();
        if (cacheManager!=null){
            String []cacheNames=cacheManager.getCacheNames();
            if( null != cacheNames && cacheNames.length>0 ){
                StringBuffer ehcacheBuffer = new StringBuffer();
                ehcacheBuffer.append(StringUtils.rightPad("CacheName", 15));
                ehcacheBuffer.append(" | ");
                ehcacheBuffer.append(StringUtils.rightPad("Key", 40));
                ehcacheBuffer.append(" | ");
                ehcacheBuffer.append(StringUtils.rightPad("HintCount", 10));
                ehcacheBuffer.append(" | ");
                ehcacheBuffer.append(StringUtils.rightPad("CreationTime", 25));
                ehcacheBuffer.append(" | ");
                ehcacheBuffer.append(StringUtils.rightPad("LastAccessTime", 25));
                ehcacheBuffer.append(" | ");
                ehcacheBuffer.append(StringUtils.rightPad("TimeToLive(ms)", 15));
                ehcacheBuffer.append(" | ");
                ehcacheBuffer.append(StringUtils.rightPad("TimeToIdle(ms)", 15));
                //这里不打印数据值,因为打印值的话数据量比较大
                ehcacheBuffer.append(" | ");
                ehcacheBuffer.append("\n");
                for (int i = 0; i < cacheNames.length; i++) {
                    Ehcache ehcache = cacheManager.getCache(cacheNames[i]);
                    if(ehcache!=null){
                        List<String> ehcacheKeys = ehcache.getKeys();
                        if( null!=ehcacheKeys && 0< ehcacheKeys.size() ){
                            for (String ehcacheKey:ehcacheKeys) {
                                Element element = ehcache.get(ehcacheKey);
                                if(element!=null ){
                                    ehcacheBuffer.append(StringUtils.rightPad(ehcache.getName(), 15));//cachenName
                                    ehcacheBuffer.append(" | ");
                                    ehcacheBuffer.append(StringUtils.rightPad(ehcacheKey, 40));//key name
                                    ehcacheBuffer.append(" | ");
                                    ehcacheBuffer.append(StringUtils.rightPad(""+element.getHitCount(), 10));//命中次数
                                    ehcacheBuffer.append(" | ");
                                    ehcacheBuffer.append(StringUtils.rightPad(formatDate(element.getCreationTime()), 25));//创建时间
                                    ehcacheBuffer.append(" | ");
                                    ehcacheBuffer.append(StringUtils.rightPad(formatDate(element.getLastAccessTime()), 25));//最后访问时间
                                    ehcacheBuffer.append(" | ");
                                    ehcacheBuffer.append(StringUtils.rightPad(""+element.getTimeToLive(), 15));   //存活时间
                                    ehcacheBuffer.append(" | ");
                                    ehcacheBuffer.append(StringUtils.rightPad(""+element.getTimeToIdle(), 15));   //空闲时间
                                    ehcacheBuffer.append(" | ");
                                    ehcacheBuffer.append("\n");
                                }
                            }
                        }


                    }
                }
                log.debug("\n"+ehcacheBuffer.toString());
                resultVo.setData(ehcacheBuffer);
                resultVo.setResult(true);
            }
        }
        return resultVo;
    }

日志效果
在这里插入图片描述

参考资料

[1]: EhCache 缓存系统简介 https://www.ibm.com/developerworks/cn/java/j-lo-ehcache/
[2]: Ehcache配置文件译文 https://dreamzhong.iteye.com/blog/1161954
[3]: EhCache 系统简介 https://www.cnblogs.com/duwanjiang/p/6230113.html
[3]: 本人其他平台早期的文档 https://blog.51cto.com/tianyang10552/1899550

标签:ehcache,缓存,ehcacheBuffer,Spring,resultVo,RMI,true,append
来源: https://blog.csdn.net/tian_111222333/article/details/97267625