ehcache介绍
一、SpringBoot默认集成CacheManager
Spring声明式缓存:Spring 定义 CacheManager 和 Cache 接口用来统一不同的缓存技术。例如 JCache、 EhCache、 Hazelcast、 Guava、 Redis 等。在使用 Spring 集成 Cache 的时候,我们需要注册实现的 CacheManager 的 Bean。
Spring Boot 为我们自动配置了多个 CacheManager 的实现。
Spring Boot 为我们自动配置了 JcacheCacheConfiguration、 EhCacheCacheConfiguration、HazelcastCacheConfiguration、GuavaCacheConfiguration、RedisCacheConfiguration、SimpleCacheConfiguration 等。
Spring针对不同的缓存技术,需要实现不同的cacheManager,Spring定义了如下的cacheManger实现:
CacheManger 描述
SimpleCacheManager 使用简单的Collection来存储缓存,主要用于测试
ConcurrentMapCacheManager 使用ConcurrentMap作为缓存技术(默认)
NoOpCacheManager 测试用
EhCacheCacheManager 使用Ehcache作为缓存技术,以前在HIbernate的时候经常用
GuavaCacheManager 使用google guava的GuavaCache作为缓存技术
HazelcastCacheManager 使用Hazelcast作为缓存技术
JCacheCacheManager 使用JCache标准的实现作为缓存技术,如Apache Commons JCS
RedisCacheManager 使用Redis作为缓存技术
常规的SpringBoot已经为我们自动配置了EhCache、Collection、Guava、ConcurrentMap等缓存,默认使用SimpleCacheConfiguration,即使用ConcurrentMapCacheManager。
SpringBoot 缓存
在 Spring Boot中,通过@EnableCaching注解自动化配置合适的缓存管理器(CacheManager),Spring Boot根据下面的顺序去侦测缓存提供者:
- Generic
- JCache (JSR-107)
- EhCache 2.x
- Hazelcast
- Infinispan
- Redis
- Guava
- Simple
关于 Spring Boot 的缓存机制:
高速缓存抽象不提供实际存储,并且依赖于由org.springframework.cache.Cache和org.springframework.cache.CacheManager接口实现的抽象。 Spring Boot根据实现自动配置合适的CacheManager,只要缓存支持通过@EnableCaching注释启用即可。
二、EHCache API的基本用法
EHCache是来自sourceforge(http://ehcache.sourceforge.net/) 的开源项目,也是纯Java实现的简单、快速的Cache组件。EHCache支持内存和磁盘的缓存,支持LRU、LFU和FIFO多种淘汰算法,支持分 布式的Cache,可以作为Hibernate的缓存插件。同时它也能提供基于Filter的Cache,该Filter可以缓存响应的内容并采用 Gzip压缩提高响应速度。
首先介绍CacheManager类。它主要负责读取配置文件,默认读取CLASSPATH下的ehcache.xml,根据配置文件创建并管理Cache对象。
// 使用默认配置文件创建CacheManager CacheManager manager = CacheManager.create(); // 通过manager可以生成指定名称的Cache对象 Cache cache = cache = manager.getCache("demoCache"); // 使用manager移除指定名称的Cache对象 manager.removeCache("demoCache");
可以通过调用manager.removalAll()来移除所有的Cache。通过调用manager的shutdown()方法可以关闭CacheManager。
有了Cache对象之后就可以进行一些基本的Cache操作,例如:
//往cache中添加元素 Element element = new Element("key", "value"); cache.put(element); //从cache中取回元素 Element element = cache.get("key"); element.getValue(); //从Cache中移除一个元素 cache.remove("key");
可以直接使用上面的API进行数据对象的缓存,这里需要注意的是对于缓存的对象都是必须可序列化的。在下面的篇幅中笔者还会介绍EHCache和Spring、Hibernate的整合使用。
三、配置文件
配置文件ehcache.xml中命名为demoCache的缓存配置:
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd"> <!--timeToIdleSeconds 当缓存闲置n秒后销毁 --> <!--timeToLiveSeconds 当缓存存活n秒后销毁 --> <!-- 缓存配置 name:缓存名称。 maxElementsInMemory:缓存最大个数。 eternal:对象是否永久有效,一但设置了,timeout将不起作用。 timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。 timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。 overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。 diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。 maxElementsOnDisk:硬盘最大缓存个数。 diskPersistent:设定在虚拟机重启时是否进行磁盘存储,默认为false diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。 clearOnFlush:内存数量最大时是否清除。 --> <!-- 磁盘缓存位置 --> <diskStore path="java.io.tmpdir" /> <!-- 默认缓存 --> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" maxElementsOnDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> <persistence strategy="localTempSwap" /> </defaultCache> <!-- 测试 --> <cache name="GoodsType" eternal="false" timeToIdleSeconds="2400" timeToLiveSeconds="2400" maxEntriesLocalHeap="10000" maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120" overflowToDisk="false" memoryStoreEvictionPolicy="LRU"> </cache> </ehcache>
各配置参数的含义:
- diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。
- defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
- name:缓存名称。
- maxElementsInMemory:缓存中允许创建的最大对象数
- eternal:缓存中对象是否为永久的,如果是,超时设置将被忽略,对象从不过期,timeout参数将不起作用
- timeToIdleSeconds:缓存数据在失效前允许的闲置时间(钝化时间)单位秒,也就是在一个元素消亡之前,两次访问时间的最大时间间隔值,这只能在元素不是永久驻留时有效,如果该值是 0 就意味着元素可以停顿无穷长的时间。
- timeToLiveSeconds:缓存数据的生存时间,也就是一个元素从构建到消亡的最大时间间隔值,这只能在元素不是永久驻留时有效,如果该值是0就意味着元素可以停顿无穷长的时间。
- overflowToDisk:内存不足(当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中)时,是否启用磁盘缓存。diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
- maxElementsOnDisk:硬盘最大缓存个数。
- diskPersistent:设定在虚拟机重启时是否进行磁盘存储,默认为false
- diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是 LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
- clearOnFlush:内存数量最大时是否清除。
- memoryStoreEvictionPolicy:缓存满了之后的淘汰算法。LRU和FIFO算法这里就不做介绍。LFU算法直接淘汰使用比较少的对象,在内存保留的都是一些经常访问的对象。对于大部分网站项目,该算法比较适用。
如果应用需要配置多个不同命名并采用不同参数的Cache,可以相应修改配置文件,增加需要的Cache配置即可。
四、Springboot整合EhCache
4.1、添加依赖
<dependency> <groupId>org.ehcache</groupId> <artifactId>ehcache</artifactId> <version>3.7.0</version> </dependency>
4.2、配置
4.2.1、xml方式配置
<?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd"> <!-- 磁盘缓存位置 --> <diskStore path="java.io.tmpdir" /> <!-- 默认缓存 --> <defaultCache maxElementsInMemory="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" maxElementsOnDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> <persistence strategy="localTempSwap" /> </defaultCache> <!-- 测试 --> <cache name="GoodsType" eternal="false" timeToIdleSeconds="2400" timeToLiveSeconds="2400" maxEntriesLocalHeap="10000" maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120" overflowToDisk="false" memoryStoreEvictionPolicy="LRU"> </cache> </ehcache>
对于EhCache的配置文件也可以通过application.yml文件中使用spring.cache.ehcache.config属性来指定,比如:
spring:
cache:
ehcache:
config: classpath:ehcache.xml
4.2.2、java config方式
@Configuration public class EhcacheConfig { @Bean("EhcacheManager") public CacheManager cacheManager() { CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder() .withCache("articles", CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, ArrayList.class, ResourcePoolsBuilder.newResourcePoolsBuilder() .heap(1, EntryUnit.ENTRIES) .offheap(2, MemoryUnit.GB) //.disk(3, MemoryUnit.GB,true) ) .build()) .build(true); return cacheManager; } }
4.3、开启缓存支持
import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Configuration; /** * * @ClassName: EhCacheConfig * @Description:配置缓存 * @author cheng * @date 2017年10月11日 下午1:00:34 */ @Configuration @EnableCaching public class EhCacheConfig { }
4.4、cache使用
一般情况下,我们在Sercive层进行对缓存的操作。
先介绍 Ehcache 在 Spring 中的注解:在支持 Spring Cache 的环境下
@Repository @CacheConfig(cacheNames = "GoodsType") public class GoodsTypeDaoImpl { @Cacheable public String save(String typeId) { System.out.println("save()执行了============="); return "模拟数据库保存"; } @CachePut public String update(String typeId) { System.out.println("update()执行了============="); return "模拟数据库更新"; } @CacheEvict public String delete(String typeId) { System.out.println("delete()执行了============="); return "模拟数据库删除"; } @Cacheable public String select(String typeId) { System.out.println("select()执行了============="); return "模拟数据库查询"; } } @RunWith(SpringRunner.class) @SpringBootTest public class GoodsTypeDaoImplTest { @Autowired private GoodsTypeDaoImpl typeDao; @Test public void testSave() { String typeId = "type111"; // 模拟第一次保存 String returnStr1 = typeDao.save(typeId); System.out.println(returnStr1); // 模拟第二次保存 String returnStr2 = typeDao.save(typeId); System.out.println(returnStr2); }
EHCache的监控
对于Cache的使用,除了功能,在实际的系统运营过程中,我们会比较关注每个Cache对象占用的内存大小和Cache 的命中率。有了这些数据,我们就可以对Cache的配置参数和系统的配置参数进行优化,使系统的性能达到最优。EHCache提供了方便的API供我们调 用以获取监控数据,其中主要的方法有:
//得到缓存中的对象数
cache.getSize();
//得到缓存对象占用内存的大小
cache.getMemoryStoreSize();
//得到缓存读取的命中次数
cache.getStatistics().getCacheHits()
//得到缓存读取的错失次数
cache.getStatistics().getCacheMisses()
分布式缓存
EHCache从1.2版本开始支持分布式缓存。分布式缓存主要解决集群环境中不同的服务器间的数据的同步问题。具体的配置如下:
在配置文件ehcache.xml中加入
<cacheManagerPeerProviderFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerProviderFactory"
properties="peerDiscovery=automatic, multicastGroupAddress=230.0.0.1, multicastGroupPort=4446"/>
<cacheManagerPeerListenerFactory
class="net.sf.ehcache.distribution.RMICacheManagerPeerListenerFactory"/>
另外,需要在每个cache属性中加入
<cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>
例如:
<cache name="demoCache"
maxElementsInMemory="10000"
eternal="true"
overflowToDisk="true">
<cacheEventListenerFactory class="net.sf.ehcache.distribution.RMICacheReplicatorFactory"/>
</cache>
总结
EHCache是一个非常优秀的基于Java的Cache实现。它简单、易用,而且功能齐全,并且非常容易 与Spring、Hibernate等流行的开源框架进行整合。通过使用EHCache可以减少网站项目中数据库服务器的访问压力,提高网站的访问速度, 改善用户的体验。