EhCache介绍
EhCache简介
EhCache是一个开源的、基于标准的缓存框架,可以提高服务的整性能。
它是使用最广泛的基于Java的缓存,因为它健壮、经过验证、功能齐全,并与其他流行的库和框架集成。
Ehcache可以从进程内缓存扩展到具有TB级缓存的混合进程内/进程外部署。
最新的Ehcache 3 本身符合JSR107(JCache规范),支持堆外存储,并提供磁盘持久化。
EhCache快速入门
本文介绍EhCache的使用,是基于EhCache3版本。
1)使用Idea工具,构建Maven工程
2)引入EhCache3,可通过[官网](https://www.ehcache.org)获取最新版本依赖,并添加单元测试框架依赖
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.10.8</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
3)编写测试代码
public class Demo {
@Test
public void test() {
// 1、初始化好 CacheManager
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.withCache("user",
CacheConfigurationBuilder.newCacheConfigurationBuilder(
String.class,
String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(100)
.build())
.build())
.build(true); // 这里可以初始化
// cacheManager.init(); // 这里也可以对CacheManager初始化,二选一
// 2、基于CacheManager 获取 Cache 对象
Cache<String, String> user = cacheManager.getCache("user", String.class, String.class);
// 3、将需要缓存的内容,放入Cache对象
user.put("zhangsan", "第一次缓存数据");
// 4、取出缓存中的内容
System.out.println(user.get("zhangsan"));
}
}
以上就是基于Java配置的EhCache3的入门案例。
EhCache基本配置
EhCache提供了非常丰富的功能,通常情况下,通过简单的配置即可使用,非常方便。
这里主要介绍两种比较重要且常用的功能:
1)缓存方式
EhCache3.x版本中不但提供了堆内缓存heap,堆外缓存off-heap,而且还提供了数据的持久化操作disk,可以将数据落到磁盘中。
heap堆内内存:
heap是将数据直接放到JVM内部,这种缓存数据在获取的时候效率最高。
heap(10) 代表当前Cache最多只能存储10个数据,当put第11个数据时,第一个数据就会被移除。
heap(10, MB) 代表当前Cache最多只能存储10MB数据。
off-heap堆外内存:
off-heap是将数据放到操作系统的一块内存区域存储,不是JVM内部。
这种对象是不能直接被JVM使用的。
存储时,需要对数据进行序列化操作,取出时,需要反序列化操作。
使用效率上比堆内内存稍低。
disk落到磁盘
disk表示将数据序列化到本地磁盘文件中,当服务重启后,会从磁盘文件中反序列化数据到内存,从而减轻数据库的压力。
EhCache提供了三种组合使用方式
heap + off-heap + disk
heap + disk【通常使用这种方式】
heap + off-heap
在组合的情况下,存储数据时,数据先落到堆内内存,同时同步到堆外内存及本地磁盘。
本地磁盘因为空间充裕,所以本地磁盘数据是最全的。
EhCache要求空间大小必须 disk > off-heap > heap。
@Test
public void test2() {
// 1、声明存储位置
String path = "D:\\ehcache";
// 2、初始化 CacheManager
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.with(CacheManagerBuilder.persistence(path)) // 落到哪里要通过CacheManager来指定
.withCache("order",
CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class,
String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(100)
.offheap(10, MemoryUnit.MB)
.disk(15, MemoryUnit.MB, true) // 落到磁盘是需要指定位置的。这里如果没有true选项,不会存储到文件
.build())
.build())
.build(true); // 这里可以初始化
// 3、基于CacheManager 获取到Cache对象
Cache<String, String> order = cacheManager.getCache("order", String.class, String.class);
// 4、将需要缓存的内容,放入Cache对象
order.put("hello", "第二次缓存数据");
// 5、取出缓存中的内容
System.out.println(order.get("hello"));
// 6、保证数据持久化不丢失,需要执行 cacheManager.close();
cacheManager.close();
}
本地磁盘存储,一共有三个文件
meta:原数据存储,记录当前cache的key类型和value类型
data:存储具体数据的位置,将数据序列化成字节存储
index:类似索引,帮助查看数据的。
ehcache-disk-store.data
ehcache-disk-store.index
ehcache-disk-store.meta
2)数据生存时间
数据如果一直存放在内存当中,因为数据会不断增长,缓存也会不断地增加,
而不使用的数据还在占用内存,这时可能会造成内存泄露等问题。
EhCache提供了对数据设置生存时间的机制
提供了三种机制:
noExpiration: 不设置生存时间
timeToLiveExpiration: 从数据落到缓存计算生存时间
timeToIdleExpiration: 从最后一个get计算生存时间
@Test
public void test3() throws InterruptedException {
// 1、初始化好 CacheManager
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.withCache("user",
CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class,
String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10)
.build())
// 不设置生存时间 三选一
// .withExpiry(ExpiryPolicy.NO_EXPIRY)
// 设置生存时间 timeToLiveExpiration
// .withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofMillis(5000)))
// 设置生存时间 timeToIdleExpiration
.withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofSeconds(10)))
.build())
.build(true); // 这里可以初始化
// 2、基于CacheManager 获取到Cache对象
Cache<String, String> user = cacheManager.getCache("user", String.class, String.class);
// 3、将需要缓存的内容,放入Cache对象
user.put("wangwu", "第三次缓存数据");
Thread.sleep(4000);
// 4、取出缓存中的内容
System.out.println(user.get("wangwu"));
Thread.sleep(9000);
System.out.println(user.get("wangwu"));
Thread.sleep(10000);
System.out.println(user.get("wangwu"));
// 5、保证数据持久化不丢失,需要执行cacheManager.close();
cacheManager.close();
Springboot集成EhCache
这里使用Springboot2.7.2 集成 EhCache3.8.1。
首先是添加springboot及缓存starter等相关依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.7.2</version>
</parent>
<groupId>com.xf</groupId>
<artifactId>ehcache</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.8.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
</project>
springboot集成EhCache有两种方式:
1)纯Java配置
通常在开发的过程中,我们不希望一些可灵活配置的数据被写死在代码中,
所以,这里配置文件application.yml中添加如下配置
hj:
ehcache:
heap: 50 # 堆内内存缓存大小限制 单位MB,即最多50MB
disk: 100 # 磁盘存储数据大小限制 单位MB,即最多100MB
diskDir: D:/data/cache/ # 磁盘存储缓存文件的位置
cacheNames: # 基于CacheManager构建多少个缓存
- SYSTEM_MANAGER
- BUSINESS
添加配置类
@Data
@Configuration
@ConfigurationProperties(prefix = "hj.ehcache")
public class EhCachePropertyConfig {
/**
* 堆内内存缓存个数
*/
private Integer heap;
/**
* 磁盘存储数据大小 单位MB
*/
private Integer disk;
/**
* 基于CacheManager构建多少个缓存
*/
private Set<String> cacheNames;
}
@Configuration
@EnableCaching
public class EhCacheConfig {
@Resource
private EhCachePropertyConfig ehCachePropertyConfig;
/**
* 配置类的方式 注入缓存管理器
* 目前无法持久化
* @return
*/
@Bean
public CacheManager ehCacheManager() {
CachingProvider cachingProvider = Caching.getCachingProvider();
javax.cache.CacheManager cacheManager = cachingProvider.getCacheManager();
org.ehcache.config.CacheConfiguration<String, Serializable> cacheConfiguration =
CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, Serializable.class,
org.ehcache.config.builders.ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(ehCachePropertyConfig.getHeap(), MemoryUnit.MB)
.build())
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(10 * 60)))
.build();
javax.cache.configuration.Configuration<String, Serializable> configuration =
Eh107Configuration.fromEhcacheCacheConfiguration(cacheConfiguration);
for (String cacheName : ehCachePropertyConfig.getCacheNames()) {
cacheManager.createCache(cacheName, configuration);
}
return new JCacheCacheManager(cacheManager);
}
}
对当前纯Java配置的方式,没有找到合适的持久化方法,如有大神知道,请指教。
使用方式如下:
@Cacheable(cacheNames = {CacheConstant.SYSTEM_MANAGER}, key = "T(com.alibaba.fastjson.JSON).toJSONString(#roleIds)")
@Override
public Set<String> listPermissionRecordFromRole(List<String> roleIds) {
log.info("SysRoleManagerImpl.listPermissionRecordFromRole, roleIds={}", JSON.toJSONString(roleIds));
return sysRoleMapper.listPermissionRecordFromRole(roleIds);
}
2)使用XML配置
XML配置的方式比较简单,主要是XML配置项,其次需要在application.yml文件中指定cache类型和配置文件位置,
具体配置方式如下:
application.yml配置
spring:
cache:
type: jcache
jcache:
config: classpath:ehcache.xml
ehcache.xml配置
在工程的resources目录下新建ehcache.xml文件,配置方式如下:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.ehcache.org/v3"
xmlns:jsr107="http://www.ehcache.org/v3/jsr107"
xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd
http://www.ehcache.org/v3/jsr107 http://www.ehcache.org/schema/ehcache-107-ext-3.0.xsd">
<!-- 缓存持久化配置: 定义磁盘缓存位置 -->
<persistence directory="./tmp/ehcache"/>
<!-- 缓存模板: 未填写缓存名时使用的默认缓存,同时也可被继承 也可以不用模版直接在cache中配置与模版参数相同 -->
<!-- <cache-template name="defaultCache">-->
<!-- <key-type>java.lang.String</key-type>-->
<!-- <value-type>java.lang.Object</value-type>-->
<!-- <expiry>-->
<!-- <ttl unit="minutes">10</ttl>-->
<!-- </expiry>-->
<!-- <resources>-->
<!-- <heap unit="MB">5</heap>-->
<!-- </resources>-->
<!-- </cache-template>-->
<!-- 缓存列表: 自定义缓存配置 -->
<cache alias="SYSTEM_MANAGER">
<key-type>java.lang.String</key-type>
<value-type>java.io.Serializable</value-type>
<!--配置缓存策略
tti:缓存条目在最后一次访问后到被认为过期的时间间隔
ttl:缓存条目从创建或最后一次更新开始,到被认为过期的时间间隔
系统管理缓存保存20分钟
-->
<expiry>
<tti unit="minutes">20</tti>
</expiry>
<resources>
<heap unit="MB">500</heap>
<disk unit="GB" persistent="true">1</disk>
</resources>
</cache>
<cache alias="LDAR_BUSINESS">
<key-type>java.lang.String</key-type>
<value-type>java.io.Serializable</value-type>
<!--配置缓存策略
tti:缓存条目在最后一次访问后到被认为过期的时间间隔
ttl:缓存条目从创建或最后一次更新开始,到被认为过期的时间间隔
业务缓存保存5分钟
-->
<expiry>
<tti unit="minutes">10</tti>
</expiry>
<resources>
<heap unit="MB">500</heap>
<disk unit="GB" persistent="true">1</disk>
</resources>
</cache>
</config>
使用方式同上。