目录
- [1、JetCache 简介](#1、JetCache 简介)
- [2、JetCache 核心概念](#2、JetCache 核心概念)
- 3、入门案例
-
- 3.1、引入依赖
- 3.2、修改配置文件
- 3.3、添加配置扫描注解
- [3.4、使用 JetCache](#3.4、使用 JetCache)
-
- 3.4.1、基于注解实现缓存
-
- [3.4.1.1、添加 `UserServiceImpl`:](#3.4.1.1、添加
UserServiceImpl
:) - [3.4.1.2、添加 `TestController`:](#3.4.1.2、添加
TestController
:) - [3.4.1.1、`@Cached` 注解](#3.4.1.1、
@Cached
注解) - [3.4.1.1.1、cacheType 属性](#3.4.1.1.1、cacheType 属性)
- [3.4.1.2、`@CacheUpdate` 注解](#3.4.1.2、
@CacheUpdate
注解) - [3.4.1.3、`@CacheInvalidate` 注解](#3.4.1.3、
@CacheInvalidate
注解)
- [3.4.1.1、添加 `UserServiceImpl`:](#3.4.1.1、添加
- [3.4.2、基于 `@CreateCache` 注解实现缓存](#3.4.2、基于
@CreateCache
注解实现缓存) - [3.4.3、基于 API 方式实现缓存](#3.4.3、基于 API 方式实现缓存)
-
- [3.4.3.1、添加一个配置类 `JetcacheConfig`:](#3.4.3.1、添加一个配置类
JetcacheConfig
:) - [3.4.3.2、添加 `TestController`](#3.4.3.2、添加
TestController
)
- [3.4.3.1、添加一个配置类 `JetcacheConfig`:](#3.4.3.1、添加一个配置类
1、JetCache 简介
JetCache
是由阿里巴巴开源的一款基于 Spring 和 Redis 的分布式缓存框架 ,提供统一的 API 和注解来简化缓存的使用。
JetCache
提供了比 Spring Cache
更加强大的注解,可以原生的支持 TTL、两级缓存、分布式自动刷新,还提供了 Cache 接口用于手工缓存操作。 当前有四个实现,RedisCache
、TairCache
(此部分未在 github 开源)、CaffeineCache
(in memory) 和一个简易的 LinkedHashMapCache
(in memory),要添加新的实现也是非常简单的
它的特性:
- 通过统一的 API 访问 Cache 系统
- 通过注解实现声明式的方法缓存,支持 TTL 和两级缓存
- 通过注解创建并配置 Cache 实例
- 针对所有 Cache 实例和方法缓存的自动统计
- Key 的生成策略和 Value 的序列化策略是可以配置的
- 分布式缓存自动刷新,分布式锁 (2.2+)
- 异步 Cache API (2.2+,使用 Redis 的 lettuce 客户端时)
2、JetCache 核心概念
- 缓存抽象 :
JetCache-Alibaba
提供了一个统一的缓存抽象层,这意味着你不需要直接和具体的缓存实现打交道。它为你封装好了底层的细节,你只需要通过它提供的 API 来操作缓存即可。这样一来,你更换缓存实现的时候,就不需要改动大量的代码了 - 分布式缓存 : 在分布式系统中,应用可能部署在多台机器上。这时候,如果每台机器都有自己的本地缓存,那数据就不一致了。
JetCache-Alibaba
支持分布式缓存,它能确保多台机器上的缓存数据是一致的。这样,无论你的应用部署在哪里,都能读到正确的缓存数据 - 缓存一致性 : 缓存里的数据和数据库里的数据得保持一致。
JetCache-Alibaba
提供了多种策略来确保缓存的一致性,比如缓存失效、缓存更新等。根据自己的业务场景来选择合适的策略 - 降级策略 : 有时候,缓存可能会失效或者出点什么问题。这时候,应用可不能直接崩溃。
JetCache-Alibaba
提供了降级策略,当缓存不可用时,它会自动切换到数据库或者其他备份数据源,确保你的应用依然能够正常运行
3、入门案例
3.1、引入依赖
https://github.com/alibaba/jetcache/blob/master/docs/CN/GettingStarted.md
xml
<dependency>
<groupId>com.alicp.jetcache</groupId>
<artifactId>jetcache-starter-redis</artifactId>
<version>2.7.3</version>
</dependency>
<!-- jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.3.0</version>
</dependency>
这里使用了 Jedis 客户端连接 Redis
注意,如果启动出现
NoClassDefFoundError: redis/clients/util/Pool
或NoClassDefFoundError: redis/clients/jedis/UnifiedJedis
报错,说明 springboot 与 jetcache 版本不一致。如果使用的是jetcache2.7.x
版本,因为该版本中有jedis
包的依赖,需要额外添加如下依赖,或者将jetcache
版本将至 2.6.5 以下
3.2、修改配置文件
更详细的参数配置可参考官网说明:https://gitcode.com/gh_mirrors/je/jetcache/blob/master/docs/CN/Config.md
yml
jetcache:
# 统计间隔 0 表示不统计
statIntervalMinutes: 15
# 是否在缓存名称中包含 area。
# jetcache-anno 把 cacheName 作为远程缓存 key 前缀,2.4.3 以前的版本总是把 areaName 加在 cacheName 中,因此 areaName 也出现在 key 前缀中。
# 2.4.4以后可以配置,为了保持远程 key 兼容默认值为 true,但是新项目的话 false 更合理些,2.7 默认值已改为 false。
areaInCacheName: false
# 缓存类型。local 本地缓存;remote 远程缓存;local+remote 本地+远程缓存
# 本地缓存:caffeine、linkedhashmap
# 远程缓存:redis、tair
local:
# area:默认缓存配置。对应 @Cached 和 @CreateCache 的 area 属性
default:
# 本地缓存类型
type: caffeine
# key 转换器:fastjson、fastjson2、jackson
# 仅当使用 @CreateCache 且缓存类型为 local 时可以指定为 none,此时通过 equals 方法来识别 key。方法缓存必须指定 keyConvertor
keyConvertor: JACKSON
# 每个缓存实例的最大元素的全局配置,仅 local 类型的缓存需要指定
limit: 100
# 缓存过期时间,单位毫秒
expireAfterWriteInMillis: 100000
# 指定多长时间没有访问,就让缓存失效,当前只有本地缓存支持 以毫秒为单位;0 表示不使用这个功能
expireAfterAccessInMillis: 100000
# 远程缓存配置
remote:
default:
# 远程缓存类型
type: redis
keyConvertor: JACKSON
# 配置远程缓存的广播通道,通常用于分布式环境中实现缓存同步
#broadcastChannel: myBroadcastChannel
# 2.7+可选java/kryo/kryo5;2.6-可选java/kryo
valueEncoder: java
# 2.7+可选java/kryo/kryo5;2.6-可选java/kryo
valueDecoder: java
# redis 线程池
poolConfig:
minIdle: 5
maxIdle: 20
maxTotal: 50
host: 127.0.0.1
port: 6379
3.3、添加配置扫描注解
启动类添加注解 @EnableCreateCacheAnnotation
,开启缓存;添加@EnableMethodCache(basePackages = "com.zzc")
注解,配置缓存方法扫描路径
java
@EnableCreateCacheAnnotation
@EnableMethodCache(basePackages = "com.zzc")
@SpringBootApplication
public class Test2Application {
public static void main(String[] args) {
SpringApplication.run(Test2Application.class, args);
}
}
3.4、使用 JetCache
3.4.1、基于注解实现缓存
https://gitcode.com/gh_mirrors/je/jetcache/blob/master/docs/CN/MethodCache.md
JetCache
方法缓存和 Spring Cache
比较类似,它原生提供了 TTL 支持,以保证最终一致,并且支持二级缓存。JetCache2.4 以后支持基于注解的缓存更新和删除。
在 Spring 环境下,使用 @Cached
注解可以为一个方法添加缓存,@CacheUpdate
用于更新缓存,@CacheInvalidate
用于移除缓存元素。注解可以加在接口上也可以加在类上,加注解的类必须是一个Spring Bean。
3.4.1.1、添加 UserServiceImpl
:
java
@Slf4j
@Service
public class UserServiceImpl {
// 模拟数据库数据
private Map<Integer, User> userMap = new HashMap<>();
@Cached(name = "userCache:", key = "#user.id", expire = 3600, timeUnit = TimeUnit.SECONDS, cacheType = CacheType.LOCAL)
public User add(User user) {
log.info("add");
userMap.put(user.getId(), user);
return user;
}
@Cached(name = "userCache:", key = "#id", expire = 3600, timeUnit = TimeUnit.SECONDS, cacheType = CacheType.LOCAL)
public User get(Integer id) {
log.info("get");
return userMap.get(id);
}
@CacheUpdate(name = "userCache:", key = "#user.id", value = "#user")
public User update(User user) {
log.info("update");
userMap.put(user.getId(), user);
return user;
}
@CacheInvalidate(name = "userCache:", key = "#id")
public void delete(Integer id) {
log.info("delete");
userMap.remove(id);
}
}
3.4.1.2、添加 TestController
:
java
@RestController
public class TestController {
@Autowired
private UserServiceImpl userServiceImpl;
@PostMapping
public String add(@RequestBody User user) {
userServiceImpl.add(user);
return "add";
}
@GetMapping("/{id}")
public User get(@PathVariable Integer id) {
User user = userServiceImpl.get(id);
return user;
}
@PutMapping
public String update(@RequestBody User user) {
userServiceImpl.update(user);
return "update";
}
@DeleteMapping("/{id}")
public String delete(@PathVariable Integer id) {
userServiceImpl.delete(id);
return "delete";
}
}
@CacheUpdate
和@CacheInvalidate 的 name 和 area 属性必须和
@Cached` 相同,name 属性还会用做 cache 的 key 前缀
3.4.1.1、@Cached
注解
@Cached
注解的属性如下:
属性 | 默认值 | 说明 |
---|---|---|
area | "default" | 如果在配置中配置了多个缓存 area,在这里指定使用哪个 area |
name | 指定缓存的唯一名称,不是必须的,如果没有指定,会使用类名+方法名。name 会被用于远程缓存的 key 前缀。另外在统计中,一个简短有意义的名字会提高可读性。 | |
key | 使用 SpEL 指定 key,如果没有指定会根据所有参数自动生成 | |
expire | 超时时间。如果注解上没有定义,会使用全局配置,如果此时全局配置也没有定义,则为无穷大 | |
timeUnit | TimeUnit.SECONDS | 指定 expire 的单位 |
cacheType | CacheType.REMOTE | 缓存的类型,包括 CacheType.REMOTE、CacheType.LOCAL、CacheType.BOTH。如果定义为 BOTH,会使用 LOCAL 和 REMOTE 组合成两级缓存 |
localLimit | 如果 cacheType 为 LOCAL 或 BOTH,这个参数指定本地缓存的最大元素数量,以控制内存占用。如果注解上没有定义,会使用全局配置,如果此时全局配置也没有定义,则为 100 | |
localExpire | 仅当 cacheType 为 BOTH 时适用,为内存中的 Cache 指定一个不一样的超时时间,通常应该小于 expire | |
serialPolicy | 指定远程缓存的序列化方式。可选值为 SerialPolicy.JAVA 和 SerialPolicy.KRYO。如果注解上没有定义,会使用全局配置,如果此时全局配置也没有定义,则为 SerialPolicy.JAVA | |
keyConvertor | 指定 KEY 的转换方式,用于将复杂的 KEY 类型转换为缓存实现可以接受的类型,当前支持KeyConvertor.FASTJSON 和 KeyConvertor.NONE。NONE 表示不转换,FASTJSON 可以将复杂对象 KEY 转换成 String。如果注解上没有定义,会使用全局配置 | |
enabled | true | 是否激活缓存。例如某个 dao 方法上加缓存注解,由于某些调用场景下不能有缓存,所以可以设置 enabled 为 false,正常调用不会使用缓存,在需要的地方可使用 CacheContext.enableCache 在回调中激活缓存,缓存激活的标记在 ThreadLocal 上,该标记被设置后,所有 enable=false 的缓存都被激活 |
cacheNullValue | false | 当方法返回值为null的时候是否要缓存 |
condition | 使用 SpEL 指定条件,如果表达式返回 true 的时候才去缓存中查询 | |
postCondition | 使用 SpEL 指定条件,如果表达式返回 true 的时候才更新缓存,该评估在方法执行后进行,因此可以访问到 #result |
3.4.1.1.1、cacheType 属性
可以指定本地缓存、远程缓存、本地缓存 + 远程缓存。
java
@Cached(name = "userCache:", key = "#user.id", expire = 3600, timeUnit = TimeUnit.SECONDS, cacheType = CacheType.REMOTE)
public User add(User user) {
log.info("add");
userMap.put(user.getId(), user);
return user;
}
此时,User
需要实现 Serializable
接口
多级缓存的形式,会先从本地缓存获取数据,本地获取不到会从远程缓存获取;
3.4.1.2、@CacheUpdate
注解
@CacheUpdate
注解的属性如下:
属性 | 默认值 | 说明 |
---|---|---|
area | "default" | 如果在配置中配置了多个缓存 area,在这里指定使用哪个 area |
name | 指定缓存的唯一名称,不是必须的,如果没有指定,会使用类名+方法名。name 会被用于远程缓存的 key 前缀。另外在统计中,一个简短有意义的名字会提高可读性。 | |
key | 使用 SpEL 指定 key,如果没有指定会根据所有参数自动生成 | |
value | 使用 SpEL 指定 value | |
condition | 使用 SpEL 指定条件,如果表达式返回 true 的时候才去缓存中查询 |
3.4.1.3、@CacheInvalidate
注解
@CacheInvalidate
注解的属性如下:
属性 | 默认值 | 说明 |
---|---|---|
area | "default" | 如果在配置中配置了多个缓存 area,在这里指定使用哪个 area |
name | 指定缓存的唯一名称,不是必须的,如果没有指定,会使用类名+方法名。name 会被用于远程缓存的 key 前缀。另外在统计中,一个简短有意义的名字会提高可读性。 | |
key | 使用 SpEL 指定 key,如果没有指定会根据所有参数自动生成 | |
condition | 使用 SpEL 指定条件,如果表达式返回 true 的时候才去缓存中查询 |
使用
@CacheUpdate
和@CacheInvalidate
的时候,相关的缓存操作可能会失败(比如网络 IO 错误),所以指定缓存的超时时间是非常重要的
3.4.2、基于 @CreateCache
注解实现缓存
在 jetcache 2.7
版本 @CreateCache
注解已经废弃
3.4.3、基于 API 方式实现缓存
通过 CacheManager
,2.7 版本才可使用
https://github.com/alibaba/jetcache/blob/master/docs/CN/CreateCache.md
使用 CacheManager
可以创建 Cache
实例,area 和 name 相同的情况下,它和 Cached
注解使用同一个 Cache 实例
3.4.3.1、添加一个配置类 JetcacheConfig
:
java
@Configuration
public class JetcacheConfig {
@Autowired
private CacheManager cacheManager;
private Cache<Integer, Object> userCache;
@PostConstruct
public void init(){
QuickConfig qc = QuickConfig.newBuilder("userCache:")
.expire(Duration.ofSeconds(3600))
.cacheType(CacheType.BOTH)
// 本地缓存更新后,将在所有的节点中删除缓存,以保持强一致性
.syncLocal(false)
.build();
userCache = cacheManager.getOrCreateCache(qc);
}
@Bean
public Cache<Integer, Object> getUserCache(){
return userCache;
}
}
3.4.3.2、添加 TestController
java
@RestController
public class TestController {
@Autowired
private Cache<Integer, Object> userCache;
@PostMapping
public String add(@RequestBody User user) {
userCache.put(user.getId(), user);
return "add";
}
@GetMapping("/{id}")
public User get(@PathVariable Integer id) {
User user = (User) userCache.get(id);
return user;
}
@PutMapping
public String update(@RequestBody User user) {
userCache.put(user.getId(), user);
return "update";
}
@DeleteMapping("/{id}")
public String delete(@PathVariable Integer id) {
userCache.remove(id);
return "delete";
}
}