Ehcache详解

Ehcache是现在最流行的纯java开源框架,配置简单,结构清晰,功能强大。

Ehcache特性:

1)快速轻量

过去几年,众多的测试表明ehcache是最快的java缓存之一,ehcache的线程机制是为大型高并发系统设计的,大量性能测试用例保证ehcache在不同版本间性能表现的一致性,很多用户都不知道他们正在用ehcache,因此不需要什么特别的配置,api易于使用,这就很容易部署上线和运行

2)伸缩性

缓存在内存和硬盘存储可以伸缩到数Gehcache为大数据存储做过优化,大内存的情况下,所有进程可以支持数百G的吞吐,为高并发和大型多CPU服务器做优化,线程安全和性能总是一些矛盾,ehcache的线程机制设计采用了Doug lea的想法来获得较高的性能,单台虚拟机上支持多缓存管理器,通过Terracotta服务器矩阵,可以伸缩到数百个节点。

3)灵活性

ehcache具备对象api接口和可序列化api接口,不能序列化的对象可以使用出磁盘存储外ehcache的所有功能,除了元素的返回方法以外,api都是统一的,只有这2个方法不一致:getObjectValuegetKeyValue。这就使得缓存对象,序列化对象来获取新的特性这个过程简单。支持基于Cache和基于Element的过期策略,每个Cache的存活时间都是可以设置和控制的。
提供了LRULFUFIFO缓存淘汰算法,Ehcache 1.2引入了最少使用和先进先出缓存淘汰算法,构成了完整的缓存淘汰算法。提供内存和磁盘存储,Ehcache和大多数缓存解决方案一样,提供高性能的内存和磁盘存储。动态、运行时缓存配置,存活时间、空闲时间、内存和磁盘存放缓存的最大数目都是可以在运行时修改的。

4)标准支持

5)可扩展性

监听器可以插件化.ehcache1.2提供了cacheManageEventListenercacheEventListener接口,实现了可插件化。并且可以在ehcache.xml配置节点发现,冗余器和监听器都可以插件化

6)应用持久化

vm重启后,持久化到磁盘的存储可以复原数据,Ehcache是第一个引入缓存数据持久化存储的开源java缓存框架,缓存的数据可以在机器重启后从磁盘上重新获得,根据需要将缓存刷到磁盘。将缓存条目刷到磁盘的操作可以通过cache.fiush方法执行,这大大方便了ehcache的使用

7)监听器

缓存管理器监听器。允许注册实现了CacheManagerEventListener接口的监听器:
notifyCacheAdded()
notifyCacheRemoved()
缓存事件监听器。允许注册实现了CacheEventListener接口的监听器,它提供了许多对缓存事件发生后的处理机制:
notifyElementRemoved/Put/Updated/Expired 

8)开启JMX

EhcacheJMX功能是默认开启的,你可以监控和管理如下的MBean
CacheManager
CacheCacheConfigurationCacheStatistics 

9)分布式缓存

Ehcache 1.2开始,支持高性能的分布式缓存,兼具灵活性和扩展性。
分布式缓存的选项包括:
通过Terracotta的缓存集群:设定和使用Terracotta模式的Ehcache缓存。缓存发现是自动完成的,并且有很多选项可以用来调试缓存行为和性能。
使用RMIJGroups或者JMS来冗余缓存数据:节点可以通过多播或发现者手动配置。状态更新可以通过RMI连接来异步或者同步完成。
Custom
:一个综合的插件机制,支持发现和复制的能力。
可用的缓存复制选项。支持的通过RMIJGroupsJMS进行的异步或同步的缓存复制。
可靠的分发:使用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.5web容器内。Glassfish V2/3Tomcat 6Jetty 6都已经经过了测试。

10)搜索

标准分布式搜索使用了流式查询接口的方式,请参阅文档。

11Java EE和应用缓存

为普通缓存场景和模式提供高质量的实现。

阻塞缓存:它的机制避免了复制进程并发操作的问题。
SelfPopulatingCache
在缓存一些开销昂贵操作时显得特别有用,它是一种针对读优化的缓存。它不需要调用者知道缓存元素怎样被返回,也支持在不阻塞读的情况下刷新缓存条目。
CachingFilter
:一个抽象、可扩展的cache filter
SimplePageCachingFilter
:用于缓存基于request URIQuery String的页面。它可以根据HTTP request header的值来选择采用或者不采用gzip压缩方式将页面发到浏览器端。你可以用它来缓存整个Servlet页面,无论你采用的是JSPvelocity,或者其他的页面渲染技术。
SimplePageFragmentCachingFilter
:缓存页面片段,基于request URIQuery String。在JSP中使用jsp:include标签包含。
已经使用OrionTomcat测试过,兼容Servlet 2.3Servlet 2.4规范。
Cacheable
命令:这是一种老的命令行模式,支持异步行为、容错。
兼容Hibernate,兼容Google App Engine
基于JTA的事务支持,支持事务资源管理,二阶段提交和回滚,以及本地事务。

12)开源协议

Apache 2.0 license

 

EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider

Ehcache是一种广泛使用的开源Java分布式缓存。主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储,缓存加载器,缓存扩展,缓存异常处理程序,一个gzip缓存servlet过滤器,支持RESTSOAP api等特点。

Ehcache最初是由Greg Luck2003年开始开发。2009,该项目被Terracotta购买。软件仍然是开源,但一些新的主要功能(例如,快速可重启性之间的一致性的)只能在商业产品中使用,例如EnterpriseEHCache and BigMemory,维基媒体Foundationannounced目前使用的就是Ehcache技术。

 

主要的特性有:

1. 快速

2. 简单

3. 多种缓存策略

4. 缓存数据有两级:内存和磁盘,因此无需担心容量问题

5. 缓存数据会在虚拟机重启的过程中写入磁盘

6. 可以通过RMI、可插入API等方式进行分布式缓存

7. 具有缓存和缓存管理器的侦听接口

8. 支持多缓存管理器实例,以及一个实例的多个缓存区域

9. 提供Hibernate的缓存实现

Ehcache测试

由于配置文件中只能指定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

测试代码块:


<?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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/cache
           http://www.springframework.org/schema/cache/spring-cache-3.1.xsd">

    <cache:annotation-driven cache-manager="cacheManager"/>

    <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
        <property name="cacheManager" ref="ehcache"></property>
    </bean>

    <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="classpath:ehcache-setting.xml"></property>
    </bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
    xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"  
    updateCheck="false" dynamicConfig="false" monitoring="autodetect">
        <!--  
        diskStore path:用来配置磁盘缓存使用的物理路径  
        name:   缓存名称,cache的唯一标识(ehcache会把这个cache放到HashMap里)  
        eternal="false"   元素是否永恒,如果是就永不过期(必须设置)  
        maxElementsOnDisk====磁盘缓存中最多可以存放的元素数量,0表示无穷大   
        maxElementsInMemory="1000" 内存缓存中最多可以存放的元素数量(必须设置)  
        timeToIdleSeconds="0"   导致元素过期的访问间隔(秒为单位). 0表示可以永远空闲,默认为0  
        timeToLiveSeconds="600" 元素在缓存里存在的时间(秒为单位). 0 表示永远存在不过期  
        overflowToDisk="false"  当缓存达到maxElementsInMemory值是,是否允许溢出到磁盘(必须设置)  
        diskPersistent="false"  磁盘缓存在VM重新启动时是否保持(默认为false)  
        diskExpiryThreadIntervalSeconds="100" 磁盘失效线程运行时间间隔,默认是120秒  
        memoryStoreEvictionPolicy="LFU" 内存存储与释放策略.当达到maxElementsInMemory时  
               共有三种策略,分别为LRU(最近最少使用)、LFU(最常用的)、FIFO(先进先出)默认使用"最近使用"策略  
    -->
    <!-- 指定一个文件目录,当EhCache把数据写到硬盘上时,将把数据写到这个文件目录下E:/ehcache_log   /opt/logs/epp/log/sps/ehcache -->
    <diskStore path="E:/ehcache_log"/>

    <!-- 设定缓存的默认数据过期策略 -->
    <defaultCache
            maxElementsInMemory="1000"
            eternal="false"	
            overflowToDisk="false"
            timeToIdleSeconds="0"
            timeToLiveSeconds="300"
            diskPersistent="false"
            diskExpiryThreadIntervalSeconds="120"/>
            
	<!-- <cache name="ehcache"
           maxElementsInMemory="1000"
           eternal="false"
           overflowToDisk="false"
           timeToIdleSeconds="300"
           timeToLiveSeconds="300"/> -->
<!-- 测试缓存溢出到磁盘上的参数 -->
    <cache name="ehcache"
           maxElementsInMemory="0"
           eternal="false"
           overflowToDisk="true"
           timeToIdleSeconds="300"
           timeToLiveSeconds="300"/>
</ehcache>
    @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在内存中查询是否存在此缓存值,如果不存在就进入方法内部执行获取返回值,如果已存在该缓存,就不进入方法内部,直接获取缓存中的值就行,例如,入参activityCodekey1,第一次由于没有缓存,就进入方法内部执行,最后返回结果为result1,同时将key1= result1放入缓存中,key1在缓存中存在的时间是5分钟,第二次在5分钟内有调用方法入参还是key1,在缓存中找到了key1对应的result1,直接返回result1结果,不进入方法内部,如果入参为key2,在缓存中没有对应的值,就同上进入方法内部。





Ehcache集群


我司在ehcache基础上开发了snf-ehcache,通过SCM上配置的路由信息,建立ehcache集群,实现不同IP之间的ehcache缓存的同步,通过连接到相同环境的SCM上建立路由连接,当其中一方的修改缓存中key1对应的result1result2时,另一方获取时就会立即发现key1对应的值改变了,会重新获取最新的key1值,并更新到本机的ehcache缓存中

主要是下面的两个配置在起作用

    <cacheManagerPeerProviderFactory class="com.suning.framework.ehcache.ZKCacheManagerPeerProviderFactory"/>
    <cacheManagerPeerListenerFactory class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"/>

cacheManagerPeerProviderFactory配置的是以SCMZK服务器的缓存为主,当集群中的某个机器更新了key1值时,会将该值传给ZK服务器,集群中的其它机器会通过cacheManagerPeerListenerFactory建立监听器, ZK服务器会将当前缓存的更新信息发给集群中的其他机器,这样就实现ehcache集群同步更新缓存信息

参考文章
https://blog.csdn.net/shenbushen/article/details/52140078
https://blog.csdn.net/u014209975/article/details/53320395
https://blog.csdn.net/zl834205311/article/details/70308342



猜你喜欢

转载自blog.csdn.net/weixin_38065504/article/details/80054843