A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

© 大蓝鲸Java 中级黑马   /  2019-2-22 12:20  /  1053 人查看  /  0 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

EhCache详细解读

特性一览

1、快速轻量

2、伸缩性

3、灵活性

4、标准支持

5、可扩展性

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

最小的依赖:唯一的依赖就是SLF4J了。
缓存在内存和磁盘存储可以伸缩到数G,Ehcache为大数据存储做过优化。大内存的情况下,所有进程可以支持数百G的吞吐。为高并发和大型多CPU服务器做优化。线程安全和性能总是一对矛盾,Ehcache的线程机制设计采用了Doug Lea的想法来获得较高的性能。单台虚拟机上支持多缓存管理器。

通过Terracotta服务器矩阵,可以伸缩到数百个节点。Ehcache 1.2具备对象API接口和可序列化API接口。
不能序列化的对象可以使用除磁盘存储外Ehcache的所有功能。

除了元素的返回方法以外,API都是统一的。只有这两个方法不一致:getObjectValue和getKeyValue。这就使得缓存对象、序列化对象来获取新的特性这个过程很简单。
支持基于Cache和基于Element的过期策略,每个Cache的存活时间都是可以设置和控制的。提供了LRU、LFU和FIFO缓存淘汰算法,Ehcache 1.2引入了最少使用和先进先出缓存淘汰算法,构成了完整的缓存淘汰算法。

提供内存和磁盘存储,Ehcache和大多数缓存解决方案一样,提供高性能的内存和磁盘存储。
动态、运行时缓存配置,存活时间、空闲时间、内存和磁盘存放缓存的最大数目都是可以在运行时修改的。
Ehcache提供了对JSR107 JCACHE API最完整的实现。因为JCACHE在发布以前,Ehcache的实现(如
net.sf.jsr107cache)已经发布了。

实现JCACHE API有利于到未来其他缓存解决方案的可移植性。hcache的维护者Greg Luck,正是JSR107的专家委员会委员。监听器可以插件化。

6、应用持久化

7、监听器

8、开启JMX

9、分布式缓存

Ehcache 1.2提供了CacheManagerEventListener和CacheEventListener接口,实现可以插件,并且可以在ehcache.xml里配置。节点发现,冗余器和监听器都可以插件化。
分布式缓存,从Ehcache 1.2开始引入,包含了一些权衡的选项。Ehcache的团队相信没有什么是万能的配置。实现者可以使用内建的机制或者完全自己实现,因为有完整的插件开发指南。
缓存的可扩展性可以插件化。创建你自己的缓存扩展,它可以持有一个缓存的引用,并且绑定在缓存的生命周期内。缓存加载器可以插件化。创建你自己的缓存加载器,可以使用一些异步方法来加载数据到缓存里面。缓存异常处理器可以插件化。创建一个异常处理器,在异常发生的时候,可以执行某些特定操作。在VM重启后,持久化到磁盘的存储可以复原数据。Ehcache是第一个引入缓存数据持久化存储的开源Java缓存框架。缓存的数据可以在机器重启后从磁盘上重新获得。根据需要将缓存刷到磁盘。将缓存条目刷到磁盘的操作可以通过cache.flush()方法来执行,这大大方便了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
容器里面配置。
标准服务端包含了内嵌的Glassfish web容器。它被打成了war包,可以任意部署到支持Servlet 2.5的web容器内。

10、搜索

11、Java EE和应用缓存

12、开源协议

Ehcache的加载模块列表
ehcache-core:API,标准缓存引擎,RMI复制和Hibernate支持
ehcache:分布式Ehcache,包括Ehcache的核心和Terracotta的库
ehcache-monitor:企业级监控和管理
ehcache-web:为Java Servlet Container提供缓存、gzip压缩支持的filters
ehcache-jcache:JSR107 JCACHE的实现
ehcache-jgroupsreplication:使用JGroup的复制
ehcache-jmsreplication:使用JMS的复制
ehcache-openjpa:OpenJPA插件
ehcache-server:war内部署或者单独部署的RESTful cache server
ehcache-unlockedreadsview:允许Terracotta cache的无锁读
ehcache-debugger:记录RMI分布式调用事件
Ehcache for Ruby:Jruby and Rails支持
Ehcache的结构设计概览:
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
北京市昌平区建材城西路金燕龙办公楼一层 电话:400-618-9090
核心定义
一致性模型
cache manager:缓存管理器,以前是只允许单例的,不过现在也可以多实例了
cache:缓存管理器内可以放置若干cache,存放数据的实质,所有cache都实现了Ehcache接口
element:单条缓存数据的组成单位
system of record(SOR):可以取到真实数据的组件,可以是真正的业务逻辑、外部接口调用、存放真实数据的数据
库等等,缓存就是从SOR中读取或者写入到SOR中去的。
说到一致性,数据库的一致性是怎样的?不妨先来回顾一下数据库的几个隔离级别:
未提交读(Read Uncommitted):在读数据时不会检查或使用任何锁。因此,在这种隔离级别中可能读取到没有提交的
数据。会出现脏读、不可重复读、幻象读。
已提交读(Read Committed):只读取提交的数据并等待其他事务释放排他锁。读数据的共享锁在读操作完成后立即释
放。已提交读是数据库的默认隔离级别。会出现不可重复读、幻象读。
可重复读(Repeatable Read):像已提交读级别那样读数据,但会保持共享锁直到事务结束。会出现幻象读。
可序列化(Serializable):工作方式类似于可重复读。但它不仅会锁定受影响的数据,还会锁定这个范围,这就阻止
了新数据插入查询所涉及的范围。
基于以上,再来对比思考下面的一致性模型:
1、强一致性模型:系统中的某个数据被成功更新(事务成功返回)后,后续任何对该数据的读取操作都得到更新后的值。
这是传统关系数据库提供的一致性模型,也是关系数据库深受人们喜爱的原因之一。强一致性模型下的性能消耗通常是
最大的。
2、弱一致性模型:系统中的某个数据被更新后,后续对该数据的读取操作得到的不一定是更新后的值,这种情况下通常
有个“不一致性时间窗口”存在:即数据更新完成后在经过这个时间窗口,后续读取操作就能够得到更新后的值。
3、最终一致性模型:属于弱一致性的一种,即某个数据被更新后,如果该数据后续没有被再次更新,那么最终所有的读
取操作都会返回更新后的值。
最终一致性模型包含如下几个必要属性,都比较好理解:
读写一致:某线程A,更新某条数据以后,后续的访问全部都能取得更新后的数据。
会话内一致:它本质上和上面那一条是一致的,某用户更改了数据,只要会话还存在,后续他取得的所有数据都必
须是更改后的数据。
单调读一致:如果一个进程可以看到当前的值,那么后续的访问不能返回之前的值。
单调写一致:对同一进程内的写行为必须是保序的,否则,写完毕的结果就是不可预期的了。
4、Bulk Load:这种模型是基于批量加载数据到缓存里面的场景而优化的,没有引入锁和常规的淘汰算法这些降低性能
缓存拓扑类型
存储方式
监控功能
监控的拓扑:
的东西,它和最终一致性模型很像,但是有批量、高速写和弱一致性保证的机制。
这样几个API也会影响到一致性的结果:
1、显式锁:如果我们本身就配置为强一致性,那么自然所有的缓存操作都具备事务性质。而如果我们配置成最终一致性
时,再在外部使用显式锁API,也可以达到事务的效果。当然这样的锁可以控制得更细粒度,但是依然可能存在竞争和线
程阻塞。
2、无锁可读取视图(UnlockedReadsView):一个允许脏读的decorator,它只能用在强一致性的配置下,它通过申请
一个特殊的写锁来比完全的强一致性配置提升性能。
3、原子方法(Atomic methods):方法执行是原子化的,即CAS操作(Compare and Swap)。CAS最终也实现了强一
致性的效果,但不同的是,它是采用乐观锁而不是悲观锁来实现的。在乐观锁机制下,更新的操作可能不成功,因为在
这过程中可能会有其他线程对同一条数据进行变更,那么在失败后需要重新执行更新操作。现代的CPU都支持CAS原语
了。
1、独立缓存(Standalone Ehcache):这样的缓存应用节点都是独立的,互相不通信。
2、分布式缓存(Distributed Ehcache):数据存储在Terracotta的服务器阵列(Terracotta Server Array,
TSA)中,但是最近使用的数据,可以存储在各个应用节点中。
3、复制式缓存(Replicated Ehcache):缓存数据时同时存放在多个应用节点的,数据复制和失效的事件以同步或者
异步的形式在各个集群节点间传播。上述事件到来时,会阻塞写线程的操作。在这种模式下,只有弱一致性模型。
它有如下几种事件传播机制:RMI、JGroups、JMS和Cache Server。
1、堆内存储:速度快,但是容量有限。
2、堆外(OffHeapStore)存储:被称为BigMemory,只在企业版本的Ehcache中提供,原理是利用nio的
DirectByteBuffers实现,比存储到磁盘上快,而且完全不受GC的影响,可以保证响应时间的稳定性;但是direct
buffer的在分配上的开销要比heap buffer大,而且要求必须以字节数组方式存储,因此对象必须在存储过程中进行序
列化,读取则进行反序列化操作,它的速度大约比堆内存储慢一个数量级。(注:direct buffer不受GC影响,但是
direct buffer归属的的JAVA对象是在堆上且能够被GC回收的,一旦它被回收,JVM将释放direct buffer的堆外空
间。)
3、磁盘存储。
每个应用节点部署一个监控探针,通过TCP协议与监控服务器联系,最终将数据提供给富文本客户端或者监控操作
服务器。
简单使用案例

[AppleScript] 纯文本查看 复制代码
package com.spring.cache;
import static org.junit.Assert.assertTrue;
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
import net.sf.ehcache.pool.Pool;
import org.slf4j.Logger;
public class TestDefaultCache {
private Logger Log;
//测试类
public static void main(String[] args) {
TestDefaultCache.cacheLocate("C:\\DATA\\cache");
}
// 使用缓存的基本操作
public static void testDefault(){
//从classes目录中查找encache.xml配置文件
CacheManager cacheManager=CacheManager.getInstance();
//根据配置文件获Cache实例
Cache cache=cacheManager.getCache("test");
//清空cache中的所用元素
cache.removeAll();
//往cache中添加元素,这里的Element相当于HashMap中的键值对Entry<K,V>;
cache.put(new Element("s1","1111"));
cache.put(new Element("s2","2222"));
System.out.println("calculateOnDiskSize: "+cache.calculateOnDiskSize());
System.out.println("MemoryStoreSize: "+cache.getMemoryStoreSize());
assertTrue(cache.getMemoryStoreSize() + cache.calculateOnDiskSize() == 2);
EhCache.xml文件
assertTrue(cache.getMemoryStoreSize() + cache.calculateOnDiskSize() == 2);
//System.out.println("MemoryStoreSize: "+cache.get);
//从cache中取得元素
Element e=cache.get("s2");
System.out.println(e.getValue());
//输出当前cacheManager正在使用的配置对应的Xml格式文本
System.out.println(cacheManager.getActiveConfigurationText());
//卸载缓存管理器
cacheManager.shutdown();
}
//将缓存写到本地硬盘上指定的位置。
public static void cacheLocate(String targetlocate){
/*系统默认的缓存地址可以在System.getProperty(String key)方法中找到,其中key为java.io.tmpdir
* 这里我们也可以通过 System.setProperty("java.io.tmpdir", "C:\\DATA\\cache");这个方法去改变
我们想缓存的地方
* */
System.out.println(System.getProperty("java.io.tmpdir"));
System.setProperty("java.io.tmpdir", targetlocate);//设置缓存的位置
System.out.println(System.getProperty("java.io.tmpdir"));
CacheManager cacheManager=new CacheManager();
Cache cache=cacheManager.getCache("simpleCache");
for(int i=0;i<5;i++){
Element element=new Element("key"+i,"myvalue"+i);
cache.put(element);
} c
ache.flush();
System.out.println("已经成功缓存");
//==============================================================================================
=
System.out.println("=============================读取
cache=================================");
System.out.println("DiskStoreSize:"+cache.getDiskStoreSize());
System.out.println("MemoryStoreSize:"+cache.getMemoryStoreSize());
String [] strs=cacheManager.getCacheNames();//拿到所有cache名字
for (String str : strs) {
System.out.println(str);
} c
acheManager.shutdown();
}
} <
!‐‐ <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema‐instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
maxBytesLocalDisk="20G" maxBytesLocalOffHeap="50M">
<diskStore path="java.id.tmpdir"/>
<defaultCache copyOnRead="true" copyOnWrite="true" overflowToOffHeap="false"></defaultCache>
<cache name="test" maxBytesLocalHeap="10M"/>
<cache name="test2" overflowToOffHeap="false" maxBytesLocalHeap="10M"></cache>
<cache name="sampleCache" maxElementsInMemory="10000" maxElementsOnDisk="1000"
eternal="false"
overflowToDisk="true" diskSpoolBufferSizeMB="20"
timeToIdleSeconds="300" timeToLiveSeconds="600" memoryStoreEvictionPolicy="FIFO" />
</ehcache> ‐‐> <pre name="code" class="html"> <ehcache
xmlns:xsi="http://www.w3.org/2001/XMLSchema‐instance"
xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<diskStore path="java.io.tmpdir"/>
<Cache name="test" maxElementsInMemory="1000"></Cache>
<!‐‐ 缓存到本地硬盘上 ‐‐>
<Cache name="simpleCache"
maxElementsInMemory="2"
eternal="false"
timeToIdleSeconds="600"
timeToLiveSeconds="300"
overflowToDisk="true"
maxElementsOnDisk="3"
diskPersistent="true"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="FIFO"
/>
<!‐‐
<Cache name="simpleCache" 缓存的名字
maxElementsInMemory="10000" 内存中最大的对象数
eternal="false" 对象是否永久存活
timeToIdleSeconds="120" 最大的空闲时间
timeToLiveSeconds="120" 最长的活跃时间
overflowToDisk="true" 当大小超过memory的大小时,可以缓存到本地硬盘
maxElementsOnDisk="10000000" 硬盘上最大的对象数
diskPersistent="false" 是否在硬盘上持久化保存
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU" 内存清除对象的方式
/> ‐‐>
</ehcache>















0 个回复

您需要登录后才可以回帖 登录 | 加入黑马