xyZGHio

本是青灯不归客,却因浊酒恋风尘

0%

Ehcache在SpringBoot中的实践

Ehcache,一个纯Java的进程内存缓存框架。本地缓存相比较于分布式缓存(例如Redis等)而言,由于避免网络IO通讯的开销故效率非常高。这里就如何在SpringBoot下进行集成、应用来进行介绍

abstract.jpeg

依赖

在POM文件中添加如下依赖

1
2
3
4
5
6
7
8
9
10
<!--EhCache-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.6</version>
</dependency>

配置

这里我们使用XML配置文件的方式来进行配置。在src/main/resources目录下创建名为ehcache.xml的Ehcache配置文件,示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
maxBytesLocalHeap="30K"> <!--该CacheManager中所有Cache可使用的内存共计为200K-->

<!-- 磁盘缓存文件的路径 -->
<diskStore path="user.dir/Ehcache"/>

<!--
默认的缓存配置:
该配置将应用于 通过CacheManager.add(String cacheName)
以编程方式创建的缓存
-->
<defaultCache eternal="false"
timeToIdleSeconds="123" timeToLiveSeconds="311"
maxBytesLocalHeap="13K" overflowToDisk="false" diskPersistent="false"
memoryStoreEvictionPolicy="LRU">
</defaultCache>

<!--名为stuInfo的缓存-->
<cache name="stuInfo" eternal="false"
timeToIdleSeconds="0" timeToLiveSeconds="300"
maxBytesLocalHeap="12K" overflowToDisk="false" diskPersistent="false"
memoryStoreEvictionPolicy="LRU">
</cache>
</ehcache>

这里就常见的配置项,说明如下

diskStore path

当 内存缓存中的数量超过maxElementsInMemory 且 overflowToDisk为true时 时,会将缓存数据存储到磁盘缓存中。当然被缓存的对象需要实现Serializable序列化接口)。该配置指定磁盘缓存文件的路径。其可选值通常有:

  • user.home:用户主目录
  • user.dir:用户当前的工作目录
  • java.io.tmpdir:默认临时路径
  • 自定义绝对路径

对于上述几个变量的具体路径可通过下述方式来了解、确定

1
2
3
4
5
public static void main(String[] args) {
System.out.println("<user.home>: " + System.getProperty("user.home"));
System.out.println("<user.dir>: " + System.getProperty("user.dir"));
System.out.println("<java.io.tmpdir>" + System.getProperty("java.io.tmpdir"));
}

测试结果如下

figure 1.jpeg

defaultCache

默认的缓存配置,其是可选地。该配置将应用于 通过CacheManager.add(String cacheName)以编程方式创建的缓存。如果使用了CacheManager.add(String cacheName)而无该配置将会引起CacheException

name

缓存名称,用于唯一标识缓存

eternal

缓存中的数据是否永久有效。当其为true时,将会忽略timeToIdleSeconds、timeToLiveSeconds配置

timeToIdleSeconds

缓存数据在失效前允许的闲置时间(Unit: second)。若缓存数据超过该时间间隔后未被访问,则该缓存数据即会被从Cache中清除。默认值为0,意为无穷大。显然该配置仅仅在eternal为false时生效

timeToLiveSeconds

缓存数据的生存时间(Unit: second)。默认值为0,意为无穷大。显然该配置仅仅在eternal为false时生效

maxBytesLocalHeap

用于指定CacheManager下所有Cache 或 某个Cache 在内存中缓存数据的容量上限。显然各Cache所使用总内存(12KB+13KB)不可超过CacheManager所指定的总内存容量(30KB)

maxEntriesLocalHeap

其还支持通过对Cache中缓存数据的数量进行限制,即在Cache标签内的maxEntriesLocalHeap属性(例如,maxEntriesLocalHeap=”100”),其中0意为无上限。值得注意的是,上述两种限制方式:内存容量(maxBytesLocalHeap)、缓存数量(maxEntriesLocalHeap),只可使用其中一种。即不可同时使用、指定上述两种属性的值

overflowToDisk

内存缓存中缓存数据超过上限(maxElementsInMemory)时,是否使用硬盘保存。值为true时,使用硬盘来保存缓存数据;值为false时,会根据memoryStoreEvictionPolicy配置的淘汰策略来淘汰内存缓存中的数据

diskPersistent

磁盘持久化。当其为true时,在重启虚拟机后会将硬盘缓存加载到内存缓存中

memoryStoreEvictionPolicy

缓存淘汰策略。当内存缓存数据的数量达到上限maxElementsInMemory 且 overflowToDisk为false时,Ehcache即会利用该配置执行相应的淘汰策略。其可选值有:

  • FIFO:先进先出策略
  • LFU:最近最不常用策略
  • LRU:最近最少使用策略

基本操作

在Ehcache中通常有以下几个概念

  • CacheManager:缓存管理器,是Ehcache框架的核心类和入口。可通过Ehcache配置文件来获取该类的实例
  • Cache:每个CacheManager可以管理若干个Cache
  • Element:每个Cache可以管理若干个Element,真正用于存放缓存数据

figure 2.jpeg

基本使用方法如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class TestEhcache {
public static void main(String[] args) {

// 获取缓存管理器
CacheManager manager = CacheManager.newInstance("src/main/resources/ehcache.xml");
// 通过缓存管理器获取Cache
Cache stuInfoCache = manager.getCache("stuInfo");

StuInfo stuInfo1 = new StuInfo(1, "Aaron", 25);
// 构造缓存元素, key: id, value: stuInfo对象
Element element1 = new Element(stuInfo1.getId(), stuInfo1 );
// 放入缓存
stuInfoCache.put(element1);

// 根据key从Cache获取相应的Element
Element element2 = stuInfoCache.get( (Object)stuInfo1.getId() );
// Element的value即为所需的缓存数据
StuInfo stuInfo2 = (StuInfo) element2.getObjectValue();

System.out.println("stuInfo2 : " + stuInfo2);
}
}

/**
* 学生信息
*/
@Data // 生成 get/set 方法
@AllArgsConstructor // 生成全参构造器
@NoArgsConstructor // 生成无参构造器
class StuInfo {
/**
* ID号
*/
private Integer id;

/**
* 姓名
*/
private String name;

/**
* 年龄
*/
private Integer age;
}

测试结果如下,符合预期

figure 3.png

通过Sping Cache注解操作缓存

Spring中关于缓存的操作,提供了下列注解

  • @Cacheable:如果有缓存则不执行方法、直接获取缓存数据; 否则执行方法并设置缓存数据以备下一次访问。适用于读数据的方法
  • @CachePut:执行方法,并设置缓存。适用于新增、修改数据的方法
  • @CacheEvict:执行方法,并删除缓存。适用于删除数据的方法

使用示例如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class StuInfoService {

/**
* 缓存名称
*/
private static final String cacheName = "stuInfo";

@Cacheable(value = cacheName, key = "#id")
public StuInfo getStuInfoById(Integer id) {
System.out.println("begin <getStuInfoById>");
...
System.out.println("end <getStuInfoById>");
return stuInfo;
}

@CachePut(value = cacheName, key = "#id")
public StuInfo updateStuInfoById(StuInfo stuInfo) {
System.out.println("begin <updateStuInfoById>");
...
System.out.println("end <updateStuInfoById>");
return stuInfo;
}

/**
* 删除指定缓存下的指定key的缓存数据
*/
@CacheEvict(value = cacheName, key = "#id")
public void deleteStuInfoById(Integer id) {
System.out.println("begin <deleteStuInfoById>");
...
System.out.println("end <deleteStuInfoById>");
}

/**
* 删除指定缓存下的所有缓存数据
*/
@CacheEvict(value = cacheName, allEntries = true)
public void deleteAll() {
System.out.println("begin <deleteAll>");
...
System.out.println("end <deleteAll>");
}
}
请我喝杯咖啡捏~

欢迎关注我的微信公众号:青灯抽丝