当前最常用的三个缓存组件: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;

这样就准确了!

原创:实现ehcache动态创建cache,以及超期判断的具体逻辑的更多相关文章

  1. Android实现多页左右滑动效果,支持子view动态创建和cache

    要实现多页滑动效果,主要是需要处理onTouchEvent和onInterceptTouchEvent,要处理好touch事件的子控件和父控件的传递问题. 滚动控制可以利用android的Scroll ...

  2. [SAP ABAP开发技术总结]反射,动态创建内表、结构、变量

    声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...

  3. JavaScript DOM动态创建(声明)Object元素

    http://www.cnblogs.com/GuominQiu/archive/2011/04/01/2002783.html 一文提及“等整个页面加载完毕后,根据用户所选的阅读机类型,再用Java ...

  4. 使用dxNavBar动态创建应用程序菜单

    一.如何动态创建dxNavBar内容: function TMain.GetAcitonByCaption(const aCategory,aCaption: string): Integer; va ...

  5. Qt Quick 组件和动态创建的对象具体的解释

    在<Qt Quick 事件处理之信号与槽>一文中介绍自己定义信号时,举了一个简单的样例.定义了一个颜色选择组件,当用户在组建内点击鼠标时,该组件会发出一个携带颜色值的信号,当时我使用 Co ...

  6. C#动态创建两个按钮,btn2复制btn1的Click事件,匿名委托

    现在有一个按钮btn1,要动态创建出一个btn2,需要btn2点击时调用btn1的点击. 在delphi中这种操作很简单:btn2.onClick:=btn1.onClick,因为onClick就是个 ...

  7. ehcache-----在spring和hibernate下管理ehcache和query cache

    1. 在Hibernate配置文件中设置: <!-- Hibernate SessionFactory --> <bean id="sessionFactory" ...

  8. C#动态创建Xml-LinQ方式

    C#创建Xml-LinQ方式 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- 心分享 ...

  9. C# 动态创建SQL数据库(二) 在.net core web项目中生成二维码 后台Post/Get 请求接口 方式 WebForm 页面ajax 请求后台页面 方法 实现输入框小数多 自动进位展示,编辑时实际值不变 快速掌握Gif动态图实现代码 C#处理和对接HTTP接口请求

    C# 动态创建SQL数据库(二) 使用Entity Framework  创建数据库与表 前面文章有说到使用SQL语句动态创建数据库与数据表,这次直接使用Entriy Framwork 的ORM对象关 ...

随机推荐

  1. python_20_socket

    什么是socket? -- 通过各种协议,发送和接收数据,实现网络通信 -- 在python3中,网络发送只能发二进制数据 OSI七层模型是什么? 应用 表示 会话 传输 网络             ...

  2. 搭建Hadoop集群(centos6.7+hadoop-2.7.3)

    hadoop集群有三种运行模式:单机模式.伪分布模式.完全分布模式.我们这里搭建第三种完全分布模式,即使用分布式系统,在多个节点上运行. 1 环境准备 1.1 配置DNS 进入配置文件,添加主节点和从 ...

  3. wer

    概述 快速入门流程: 使用叮当扫码产品请遵循以下操作步骤: 1. 新建项目信息 2. 新建产品信息 3. 添加发货产品 4. 发货 5. 收货 (具体使用操作请查看详细的使用说明) 文档目的: 本文档 ...

  4. tomcat中session在两个webapp中实现共享

    现在遇到一个需求就是要求完成简单的单点登录,通过在一个tomcat实例中放置两个webapps应用ROOT应用和CEO应用来完成在ROOT应用登录后,在CEO可以直接使用,而未在ROOT应用登录时,不 ...

  5. Go语言入门——dep入门

    本文出现了大量maven的内容,更适合java程序员阅读,如果你的语言做依赖管理的方案与maven差异很大,可能在有些地方会不理解 从很久之前go语言在依赖解决和管理方面方案的匮乏就被不少人诟病.光指 ...

  6. Django的ORM实现数据库事务操作

    在Django中实现数据库的事务操作 在学习MySQL数据库时,MySQL数据库是支持原子操作的. 什么是数据库的原子操作呢??打个比方,一个消费者在一个商户里刷信用卡消费. 交易正常时,银行在消费者 ...

  7. 利用 secureCRT 直接上传下载文件 (sz,rz)

    在window下向linux传送文件的方法. 首先在window中安装SecureCRT,然后在快速连接中建立一个到linux的连接,当然,你要先知道你的系统的ip,在终端中键入ifconfig可以查 ...

  8. ABP官方文档翻译 3.7 领域事件(事件总线)

    领域事件(事件总线) 事件总线 注入IEventBus 获取默认实例 定义事件 预定义事件 处理异常 实体更改 触发事件 处理事件 处理基础事件 处理者异常 处理多个事件 注册处理者 自动 手动 取消 ...

  9. linux 基础信息查询

    Linux下如何查看版本信息   Linux下如何查看版本信息, 包括位数.版本信息以及CPU内核信息.CPU具体型号等等,整个CPU信息一目了然.   1.# uname -a   (Linux查看 ...

  10. [dsu on tree]【学习笔记】

    十几天前看到zyf2000发过关于这个的题目的Blog, 今天终于去学习了一下 Codeforces原文链接 dsu on tree 简介 我也不清楚dsu是什么的英文缩写... 就像是树上的启发式合 ...