zoukankan      html  css  js  c++  java
  • 原创:实现ehcache动态创建cache,以及超期判断的具体逻辑

    当前最常用的三个缓存组件:ehcache、redis、memcached

    其中,ehcache与应用共同运行于JVM中,属于嵌入式组件,运行效率最高,因此常被用于实现一级缓存。

    在更复杂的一些系统中,由于ehcache对集群/分布式的支持相对较弱,因此还会集成redis、memcached等,实现二级缓存。

    ehcache的用法非常简单,只需要引入相关的Jar包,并创建一个配置文件,就可以在开发中使用了。

    1、在Maven中添加ehcache的依赖:

    <dependency>
        <groupId>net.sf.ehcache</groupId>
        <artifactId>ehcache</artifactId>
        <version>2.10.4</version>
    </dependency>

    这个版本是发布到net.sf.ehcache的最后一个版本,也是2.x的最后一个版本。

    org.ehcache上有更新的3.x版本,功能更强大,写法差异也挺大。

    由于2.x的核心功能已经非常稳定,已经完全满足系统需求,也更熟悉,因此我还是选择了这个2.10.4的版本。

    2、创建配置文件:ehcache.xml

    <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
             xsi:noNamespaceSchemaLocation="../config/ehcache.xsd"
             updateCheck="false" 
             dynamicConfig="false">
    
        <diskStore path="java.io.tmpdir/myApp"/>
    
        <!-- 
        默认缓存
        
        属性说明:
            maxElementsInMemory:内存中可保存的最大数量
            eternal:缓存中对象是否为永久的。如果是,超时设置将被忽略
            timeToIdleSeconds:对象最后一次访问之后的存活时间
            timeToLiveSeconds:对象创建后的存活时间
            memoryStoreEvictionPolicy:内存缓存的超期清理策略
            maxElementsOnDisk:硬盘中可保存的最大数量
            diskExpiryThreadIntervalSeconds:磁盘超期监控线程扫描时间间隔
            overflowToDisk:内存不足时,是否启用磁盘缓存
        -->
        <defaultCache 
            maxElementsInMemory="10000" 
            eternal="false"
            timeToIdleSeconds="1200" 
            maxElementsOnDisk="10000000"
            diskExpiryThreadIntervalSeconds="120" 
            memoryStoreEvictionPolicy="LRU"
            overflowToDisk="true">
        </defaultCache>
        
    </ehcache>

    把这个配置文件保存到 src/main/resource 目录下即可。

    3、实现动态创建Cache

    在ehcache中,有两个最基本的对象:Cache Element

    其中,Cache相当于ehcache的分区,可以看成memcache中的Slab,上面配置文件中的 defaultCache 就是一个默认分区

    每个Cache(分区)都类似一个Map<K, V>,可以通过Key来从Cache中返回缓存的Value

    每个KV键值对,在ehcache中,被称为Element(元素)。

    要将任何一个对象添加到ehcache中,都需要事先指定分区

    但在配置文件中创建分区很麻烦,通常只创建一个默认分区(必须存在),然后通过一个方法来动态创建分区:

    /**
     * 获取Cache,当Cache不存在时自动创建
     * 
     * @param cacheName
     * @return Cache
    * @author netwild@qq.com */ public Cache getOrAddCache(String cacheName) { Cache cache = cacheManager.getCache(cacheName); if (cache == null) { synchronized (locker) { cache = cacheManager.getCache(cacheName); if (cache == null) { cacheManager.addCacheIfAbsent(cacheName); cache = cacheManager.getCache(cacheName); } } } return cache; }

    这样的话,只需要像下面的用法,就可以很方便的把对象添加到缓存中:

    String cacheName = "article";
    String atricleId = "A00428";
    Atricle article = AtricleService.findById(atricleId);
    Element element = new Element(atricleId, article); getOrAddCache(cacheName).put(element);

    动态创建的Cache并不会出现在ehcache.xml配置文件中。

    值得注意的是,上面动态创建Cache的方法中,并没有为新的Cache指定任何参数,那这些参数的默认值是多少呢?

    其实,当创建Cache时,如果未传入参数默认值,将自动拷贝 defaultCache 的参数设置

    就是说,配置文件中 defaultCache 的超期时间等属性将直接被应用到所有动态创建的Cache。

    4、ehcache关于元素超期的判断逻辑

    在ehcache.xml配置文件中,有两个关于元素超期的参数:

    timeToLiveSeconds:对象创建后的存活时间

    timeToIdleSeconds:对象最后一次访问之后的存活时间

    这两个参数我忽略了将近一年的时间,一直在配置文件中对他们都设置了同样的参数值,比如:1200(20分钟)

    刚才反复实验多次,终于将这两个参数搞清楚,才明白以前的做法是错误的

    首先,这两个参数都可以单独设置而省略另一个,也可以分别设置成不同的值,当然也可以设置成相同的值,就像我以前做的那样

    下面分别对这几种进行说明

    1)单独设置 timeToLiveSeconds

    该对象的超期时间 = 初始创建时间 + timeToLiveSeconds

    因为初始创建时间是固定的,因此不管这个对象在有效期内被命中了多少次,一旦满足超期条件,该对象将被移除。

    2)单独设置 timeToIdleSeconds

    该对象的超时时间 = 最近访问时间 + timeToIdleSeconds

    注意与上面的区别:不再根据创建时间,而是根据最近访问时间来确定超期时间

    所以这是一种动态的超期模式,即使这个参数设置为1(秒),只要保证每秒内都能get一次,那么对象也将永远不会超期。

    3)分别设置 timeToLiveSeconds 及 timeToIdleSeconds

    那么将分别计算以上两种模式的超期时间,会得出两个结果,再从两个结果里找到最小的一个做为超期时间,相当于“严苛模式

    但事实上,创建时间肯定会小于最近访问时间,那如果两者都设置同样的参数值,相当于 timeToIdleSeconds 永远也不会起到作用。

    如果设置不同的参数值,根据具体的业务需求,可能会出现一些意料之中或者意料之外的情况。

    综上所述,我的建议是,单独设置 timeToIdleSeconds 更恰当一些,对于在有效期内被频繁命中的缓存对象,可以自动“续期”。

    5、最常用的操作之一:判断缓存中是否存在对象

    在应用缓存的开发过程中,这是最常用的操作,目的是想要知道:目标对象是否已经被缓存过

    通常下面的逻辑是:如果已被缓存过,那么直接拿出来使用;否则自力更生,完事之后再添加到缓存,下次就省事了

    很多人是这样判断的:

    return getOrAddCache(cacheName).get(key) != null;

    但这种方式存在个问题:当缓存对象实际上存在,但值就是Null,这时就相当于忽略了缓存

    所以我开始时是这样判断的:

    return getOrAddCache(cacheName).isKeyInCache(key);

    后来发现这种方式不会进行超期验证,就是说即使对象已经超期,只要当初被创建过,也会返回true

    调整之后:

    Cache cache = getOrAddCache(cacheName);
    if(cache.isKeyInCache(key) && cache.getQuiet(key) != null){
        return true;
    }
    return false;

    这样就准确了!

  • 相关阅读:
    用ps 查看线程状态
    机器学习资料收集
    [转]漫谈数据中心CLOS网络架构
    MBR中“起始磁头/扇区/柱面“同"逻辑区块地址(LBA)"的区别
    [转]硬盘分区表知识——详解硬盘MBR
    [转]什么是总线?什么是前端总线?
    语言的编译-汇编-链接
    计算机进行小数运算会出错

    计算机底层是如何访问显卡的?
  • 原文地址:https://www.cnblogs.com/netWild/p/ehcache.html
Copyright © 2011-2022 走看看