一.Ehcache是什么
EhCache 是一个纯Java的进程内缓存框架,具有快速、精干等特点,是Hibernate中默认的CacheProvider。主要面向通用缓存,Java EE和轻量级容器。它具有内存和磁盘存储、缓存加载器、缓存扩展、缓存异常处理程序、一个gzip缓存servlet过滤器、支持REST和SOAP api等特点。
Ehcache属于JVM缓存的一种,在单体项目中,JVM缓存性能比Redis更好。当然在分布式架构中,Redis仍然是首选。
二.Ehcache的特征
-
快速轻量
Ehcache的线程机制是为大型高并发系统设计的,Ehcache不需要什么复杂的配置, 很多用户甚至都不知道他们正在使用Ehcache, Ehcache的API也易于使用, 很容易部署上线和运行。
- 多种缓存策略
提供LRU、LFU和FIFO缓存策略。Ehcache支持基于Cache和基于 Element 的过期策略,每个Cache 的存活时间都是可以设置和控制的。Ehcache 提供了LRU 、 LFU 和 FIFO 缓存淘汰算法,在 Ehcaehe 1.2 引入了最少使用和先进先出缓存淘汰算 法,构成了完整的缓存淘汰算法。
-
缓存数据有两级
内存和磁盘,因此无须担心容量问题。缓存在内存和硬盘存储可 以伸缩到 GB, Ehcache 为大数据存储做过优化。在大内存的情况下,所有进程可以支持数 百 GB 的吞吐,在单台虚拟机上可以支持多缓存管理器,还可以通过 Terracotta 服务器矩阵 伸缩到数百个节点。
- 持久化,缓存数据会在虚拟机重启的过程中写入磁盘
在VM重启后,持久化到磁盘的存储可以复原数据。
Ehcache是第一个引入缓存数据持久化存储的开源Java缓存框架。缓存的数据可以在机器重启后从磁盘上重新获得。
根据需要将缓存刷到磁盘。将缓存条目刷到磁盘的操作可以通过cache.flush()方法来执行,这大大方便了Ehcache的使用
-
可以通过 RMI 、可插入 API 等多种方式进行分布式缓存
分布式缓存的选项包括:
通过 Terracotta 的缓存集群:缓存发现是自动完成的,并且有很多选项可以用来调
试缓存行为和性能。
使用 RMI、 JGroups 或者 JMS来冗余缓存数据:节点可以通过多播或发现者手动配。
置。状态更新可以通过 RMI 连接来异步或者同步完成。
可靠的分发:使用 TCP 的内建分发机制。
缓存 API: 支持 RESTFUL 和 SOAP 二种协议,没有语言限制
-
具有缓存和缓存管理器的侦听接口
缓存管理器监听器:允许注册实现了 CacheManagerEventListener 接口的监听器,方法分别是 notifyCacheAdded() 和 notifyCacheRemoved()。
缓存事件监听器:允许注册实现了 CacheEventListener 接口的监听器,它提供了许 多对缓存事件发生后的处理机制, notifyElementRemoved/Put/U pdated/Expired。
-
提供 Hibernate 的缓存实现
Hibernate 默认二级缓存是不启动的,启动二级缓存通过采用 Ehcache 来实现。
三.Ehcache架构以及分析
- Ehcache的结构设计概览
-
Ehcache的核心库,每个都为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支持
-
核心定义
cache manager:缓存管理器,以前是只允许单例的,不过现在也可以多实例了
cache:缓存管理器内可以放置若干cache,存放数据的实质,所有cache都实现了Ehcache接口
element:单条缓存数据的组成单位
system of record(SOR):可以取到真实数据的组件,可以是真正的业务逻辑、外部接口调用、存放真实数据的数据库等等,缓存就是从SOR中读取或者写入到SOR中去的。
-
存储方式
提供三种存储方式:堆内存储,堆外存储以及磁盘存储。
堆内(head)存储:速度快,但是容量有限。
堆外(OffHeapStore)存储:被称为BigMemory,只在企业版本的Ehcache中提供,原理是利用nio的DirectByteBuffers实现,比存储到磁盘上快,而且完全不受GC的影响,可以保证响应时间的稳定性;但是direct buffer的在分配上的开销要比heap buffer大,而且要求必须以字节数组方式存储,因此对象必须在存储过程中进行序列化,读取则进行反序列化操作,它的速度大约比堆内存储慢一个数量级。
(注:direct buffer不受GC影响,但是direct buffer归属的JAVA对象是在堆上且能够被GC回收的,一旦它被回收,JVM将释放direct buffer的堆外空间)。
磁盘存储:将对象写到磁盘中,当服务重启时可以直接读取磁盘上面的内容将数据加载到服务中。速度最慢,但是缓存的数据最全面。
空间大小:堆内存储<堆外存储<磁盘存储
查询效率:堆内存储>堆外存储>磁盘存储
四.SpringBoot整合Ehcache
- 引入jar包
<dependency> <groupId>org.ehcache</groupId> <artifactId>ehcache</artifactId> <version>3.X</version> </dependency> <dependency>
- 在classPath下创建ehcache.xml文件(文件路径可自定义)
配置说明:
maxElementsInMemory 内存中最大缓存对象数,根据heap大小配置 。
eternal:true表示对象永不过期,此时会忽略timeToIdleSeconds和timeToLiveSeconds属性,默认为false
maxElementsOnDisk:硬盘中最大缓存对象数,若是0表示无穷大
overflowToDisk:true表示当内存缓存的对象数目达到了maxElementsInMemory界限后,会把溢出的对象写到硬盘缓存中。注意:如果缓存的对象要写入到硬盘中的话,则该对象必须实现了Serializable接口才行。
diskSpoolBufferSizeMB:磁盘缓存区大小,默认为30MB。每个Cache都应该有自己的一个缓存区。
diskPersistent:是否缓存虚拟机重启期数据
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认为120秒
timeToIdleSeconds: 设定允许对象处于空闲状态的最长时间,以秒为单位。当对象自从最近一次被访问后,如果处于空闲状态的时间超过了timeToIdleSeconds属性值,这个对象就会过期,EHCache将把它从缓存中清空。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限期地处于空闲状态
timeToLiveSeconds:设定对象允许存在于缓存中的最长时间,以秒为单位。当对象自从被存放到缓存中后,如果处于缓存中的时间超过了 timeToLiveSeconds属性值,这个对象就会过期,EHCache将把它从缓存中清除。只有当eternal属性为false,该属性才有效。如果该属性值为0,则表示对象可以无限期地存在于缓存中。timeToLiveSeconds必须大于timeToIdleSeconds属性,才有意义
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。可选策略有:LRU(最近最少使用,默认策略)、FIFO(先进先出)、LFU(最少访问次数)。
<?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"> <!-- 指定一个文件目录,当EHCache把数据写到硬盘上时,将把数据写到这个文件目录下 --> <diskStore path="java.io.tmpdir"/> <!-- 设定缓存的默认数据过期策略 --> <defaultCache maxElementsInMemory="10000" eternal="false" overflowToDisk="true" timeToIdleSeconds="10" timeToLiveSeconds="60" diskPersistent="false" memoryStoreEvictionPolicy="LRU" diskExpiryThreadIntervalSeconds="120"/> <!--手动指定策略 --> <cache name="page-cache" maxElementsInMemory="10000" eternal="false" overflowToDisk="true" timeToIdleSeconds="300" timeToLiveSeconds="600" diskPersistent="false" memoryStoreEvictionPolicy="LRU" diskExpiryThreadIntervalSeconds="120"/> </ehcache>
- 增加spring配置
spring: cache: type: ehcache# simple: 使用Spring Boot自带的缓存 none: 禁用缓存 ehcache: 使用ehcache作为缓存 ehcache: config: classpath:ehcache.xml
-
启动类添加@EnableCaching
@EnableCaching public class Application extends SpringBootServletInitializer { … }
-
使用注解调用缓存
@Cacheable(value = "test-cache", key = "'user-info:'+#userBy",unless = "#result eq null") public User getCacheUser(Long userBy) { if (userBy != null) { return userDao.selectByPrimaryKey(userBy); } return null; }
如上例子,可以使用Cacheable、CachePut、CachEvict三种注解。
@Cacheable主要针对方法配置,能够根据方法的请求参数对其结果进行缓存
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 | 例如: @Cacheable(value=” menuCache”) 或者 @Cacheable(value={”cache1”,”cache2”} |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 |
例如: |
condition |
缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 |
例如: |
@CachePut主要针对方法配置,能够根据方法的请求参数对其结果进行缓存,@Cacheable 不同的是,它每次都会触发真实方法的调用
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 | 例如: @CachePut(value=” menuCache”) 或者 @CachePut(value={”cache1”,”cache2”} |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 |
例如: |
condition |
缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 |
例如: |
@CachEvict主要针对方法配置,能够根据一定的条件对缓存进行清空。
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 |
例如: |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写,如果不指定,则缺省按照方法的所有参数进行组合 |
例如: |
condition |
缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 |
例如: @CachEvict(value=” menuCache”,condition=”#userName.length()>2”) |
allEntries | 是否清空所有缓存内容,缺省为 false,如果指定为 true,则方法调用后将立即清空所有缓存 |
例如: @CachEvict(value=” menuCache”,allEntries=true) |
beforeInvocation | 是否在方法执行前就清空,缺省为 false,如果指定为 true,则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法执行抛出异常,则不会清空缓存 |
例如: @CachEvict(value=” menuCache”,beforeInvocation=true) |