Ehcache是现在最流行的纯java开源框架,配置简单,结构清晰,功能强大。
Ehcache特性:
过去几年,众多的测试表明ehcache是最快的java缓存之一,ehcache的线程机制是为大型高并发系统设计的,大量性能测试用例保证ehcache在不同版本间性能表现的一致性,很多用户都不知道他们正在用ehcache,因此不需要什么特别的配置,api易于使用,这就很容易部署上线和运行
缓存在内存和硬盘存储可以伸缩到数G,ehcache为大数据存储做过优化,大内存的情况下,所有进程可以支持数百G的吞吐,为高并发和大型多CPU服务器做优化,线程安全和性能总是一些矛盾,ehcache的线程机制设计采用了Doug lea的想法来获得较高的性能,单台虚拟机上支持多缓存管理器,通过Terracotta服务器矩阵,可以伸缩到数百个节点。
ehcache具备对象api接口和可序列化api接口,不能序列化的对象可以使用出磁盘存储外ehcache的所有功能,除了元素的返回方法以外,api都是统一的,只有这2个方法不一致:getObjectValue和getKeyValue。这就使得缓存对象,序列化对象来获取新的特性这个过程简单。支持基于Cache和基于Element的过期策略,每个Cache的存活时间都是可以设置和控制的。
提供了LRU、LFU和FIFO缓存淘汰算法,Ehcache 1.2引入了最少使用和先进先出缓存淘汰算法,构成了完整的缓存淘汰算法。提供内存和磁盘存储,Ehcache和大多数缓存解决方案一样,提供高性能的内存和磁盘存储。动态、运行时缓存配置,存活时间、空闲时间、内存和磁盘存放缓存的最大数目都是可以在运行时修改的。
监听器可以插件化.ehcache1.2提供了cacheManageEventListener和cacheEventListener接口,实现了可插件化。并且可以在ehcache.xml配置节点发现,冗余器和监听器都可以插件化
在vm重启后,持久化到磁盘的存储可以复原数据,Ehcache是第一个引入缓存数据持久化存储的开源java缓存框架,缓存的数据可以在机器重启后从磁盘上重新获得,根据需要将缓存刷到磁盘。将缓存条目刷到磁盘的操作可以通过cache.fiush方法执行,这大大方便了ehcache的使用
缓存管理器监听器。允许注册实现了CacheManagerEventListener接口的监听器:
notifyCacheAdded()
notifyCacheRemoved()
缓存事件监听器。允许注册实现了CacheEventListener接口的监听器,它提供了许多对缓存事件发生后的处理机制:
notifyElementRemoved/Put/Updated/Expired
Ehcache的JMX功能是默认开启的,你可以监控和管理如下的MBean:
CacheManager、Cache、CacheConfiguration、CacheStatistics
从Ehcache 1.2开始,支持高性能的分布式缓存,兼具灵活性和扩展性。
分布式缓存的选项包括:
通过Terracotta的缓存集群:设定和使用Terracotta模式的Ehcache缓存。缓存发现是自动完成的,并且有很多选项可以用来调试缓存行为和性能。
使用RMI、JGroups或者JMS来冗余缓存数据:节点可以通过多播或发现者手动配置。状态更新可以通过RMI连接来异步或者同步完成。
Custom:一个综合的插件机制,支持发现和复制的能力。
可用的缓存复制选项。支持的通过RMI、JGroups或JMS进行的异步或同步的缓存复制。
可靠的分发:使用TCP的内建分发机制。
节点发现:节点可以手动配置或者使用多播自动发现,并且可以自动添加和移除节点。对于多播阻塞的情况下,手动配置可以很好地控制。
分布式缓存可以任意时间加入或者离开集群。缓存可以配置在初始化的时候执行引导程序员。
BootstrapCacheLoaderFactory抽象工厂,实现了BootstrapCacheLoader接口(RMI实现)。
缓存服务端。Ehcache提供了一个Cache Server,一个war包,为绝大多数web容器或者是独立的服务器提供支持。
缓存服务端有两组API:面向资源的RESTful,还有就是SOAP。客户端没有实现语言的限制。
RESTful缓存服务器:Ehcached的实现严格遵循RESTful面向资源的架构风格。
SOAP缓存服务端:Ehcache RESTFul Web Services API暴露了单例的CacheManager,他能在ehcache.xml或者IoC容器里面配置。
标准服务端包含了内嵌的Glassfishweb容器。它被打成了war包,可以任意部署到支持Servlet 2.5的web容器内。Glassfish V2/3、Tomcat 6和Jetty 6都已经经过了测试。
标准分布式搜索使用了流式查询接口的方式,请参阅文档。
为普通缓存场景和模式提供高质量的实现。
阻塞缓存:它的机制避免了复制进程并发操作的问题。
SelfPopulatingCache在缓存一些开销昂贵操作时显得特别有用,它是一种针对读优化的缓存。它不需要调用者知道缓存元素怎样被返回,也支持在不阻塞读的情况下刷新缓存条目。
CachingFilter:一个抽象、可扩展的cache filter。
SimplePageCachingFilter:用于缓存基于request URI和Query String的页面。它可以根据HTTP request header的值来选择采用或者不采用gzip压缩方式将页面发到浏览器端。你可以用它来缓存整个Servlet页面,无论你采用的是JSP、velocity,或者其他的页面渲染技术。
SimplePageFragmentCachingFilter:缓存页面片段,基于request URI和Query String。在JSP中使用jsp:include标签包含。
已经使用Orion和Tomcat测试过,兼容Servlet 2.3、Servlet 2.4规范。
Cacheable命令:这是一种老的命令行模式,支持异步行为、容错。
兼容Hibernate,兼容Google App Engine。
基于JTA的事务支持,支持事务资源管理,二阶段提交和回滚,以及本地事务。
Apache 2.0 license
EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。
Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持REST和SOAP api等特点。
Ehcache最初是由Greg Luck于2003年开始开发。2009年,该项目被Terracotta购买。软件仍然是开源,但一些新的主要功能(例如,快速可重启性之间的一致性的)只能在商业产品中使用,例如EnterpriseEHCache and BigMemory。,维基媒体Foundationannounced目前使用的就是Ehcache技术。
主要的特性有:
1. 快速
2. 简单
3. 多种缓存策略
4. 缓存数据有两级:内存和磁盘,因此无需担心容量问题
5. 缓存数据会在虚拟机重启的过程中写入磁盘
6. 可以通过RMI、可插入API等方式进行分布式缓存
7. 具有缓存和缓存管理器的侦听接口
8. 支持多缓存管理器实例,以及一个实例的多个缓存区域
9. 提供Hibernate的缓存实现
由于配置文件中只能指定maxElementsInMemory,这就会有可能存入的对象太多而超出VM的 heap大小,当然你可以通过jvm参数增大heap大小,但这总还是有可能溢出。这里可以把maxElementsInMemory值设置到一个比较安 全的大小,自己预先测试一下最好。如果内存仍然存不下你需要存的对象个数,那么可以开启overflowToDisk来增加可以存储的Element个 数。这里要注意一下,EHCache不会自动帮助你去把内存对象写入到磁盘,当超过maxElementsInMemory程序会自动把更多的部分开始往 硬盘写,但是内存的对象其实并没有清出去,这时需要手动使用Cache.flush()方法来把内存对象
重建上一次运行的缓存:这个需求肯定比较普遍,我们当然不希望一旦程序退出,整个缓存就要重建了。开启diskPersistent功 能,只要使用的是CacheManager单例模式,下一次启动的时候就会调用上一次运行的缓存。比较麻烦的是写入磁盘的时间还是要自己调用 Cache.flush()方法。如果仅仅考虑到程序重启的话,我建议这里把diskStore写入到一个ramfs,这样性能就更高了,但重启电脑的话 就不得不重建缓存了。
需要的jar包:aopalliance-1.0.jar、ehcache-2.8.2.jar、spring-context-3.1.1.RELEASE.jar
测试代码块:
@Test
public void testQueryActivityInfoByCode(){
for (int i = 0; i < 5; i++) {
String result = ehcacheIntf.queryActivityInfoByCode("测试ehcache缓存"+i);
System.out.println(result);
}
String result = ehcacheIntf.queryActivityInfoByCode("测试ehcache缓存1");
System.out.println("获取测试ehcache缓存1的值为:"+result);
}
@Override
@Cacheable(value = "ehcache", key = "#activityCode")
public String queryActivityInfoByCode(String activityCode) {
LOG.debug("开始测试ehcache缓存,如果进入此步骤表示缓存中没有"+activityCode+"的值没有走缓存:[{}]", activityCode);
System.out.println("没有走缓存");
return "XXXXXXXXXXXXX"+activityCode;
}
调用分析:
调用queryActivityInfoByCode方法后,会对入参activityCode在内存中查询是否存在此缓存值,如果不存在就进入方法内部执行获取返回值,如果已存在该缓存,就不进入方法内部,直接获取缓存中的值就行,例如,入参activityCode为key1,第一次由于没有缓存,就进入方法内部执行,最后返回结果为result1,同时将key1= result1放入缓存中,key1在缓存中存在的时间是5分钟,第二次在5分钟内有调用方法入参还是key1,在缓存中找到了key1对应的result1,直接返回result1结果,不进入方法内部,如果入参为key2,在缓存中没有对应的值,就同上进入方法内部。
我司在ehcache基础上开发了snf-ehcache,通过SCM上配置的路由信息,建立ehcache集群,实现不同IP之间的ehcache缓存的同步,通过连接到相同环境的SCM上建立路由连接,当其中一方的修改缓存中key1对应的result1位result2时,另一方获取时就会立即发现key1对应的值改变了,会重新获取最新的key1值,并更新到本机的ehcache缓存中
主要是下面的两个配置在起作用
cacheManagerPeerProviderFactory配置的是以SCM上ZK服务器的缓存为主,当集群中的某个机器更新了key1值时,会将该值传给ZK服务器,集群中的其它机器会通过cacheManagerPeerListenerFactory建立监听器, ZK服务器会将当前缓存的更新信息发给集群中的其他机器,这样就实现ehcache集群同步更新缓存信息
参考文章