1. 简介
J2Cache 是 OSChina 目前正在使用的两级缓存框架(要求至少 Java 8)。第一级缓存使用内存(同时支持 Ehcache 2.x、Ehcache 3.x 和 Caffeine),第二级缓存使用 Redis(推荐)/Memcached 。由于大量的缓存读取会导致 L2 的网络成为整个系统的瓶颈,因此 L1 的目标是降低对 L2 的读取次数。该缓存框架主要用于集群环境中。单机也可使用,用于避免应用重启导致的缓存冷启动后对后端业务的冲击。
数据读取
- 读取顺序 -> L1 -> L2 -> DB
- 数据更新
从数据库中读取最新数据,依次更新 L1 -> L2 ,发送广播清除某个缓存信息
接收到广播(手工清除缓存 & 一级缓存自动失效),从 L1 中清除指定的缓存信息
2. 实战案例
2.1 依赖管理
net.oschina.j2cache
j2cache-core
2.8.5-release
net.oschina.j2cache
j2cache-spring-boot2-starter
2.8.0-release
2.2 配置
redis:
# 地址, 多个地址使用‘,’逗号分割
hosts: localhost:6379
# 数据库索引
database: 11
# 密码
password: xxxooo
# 连接超时时间
timeout: 10s
# 连接池中的最小空闲连接
min-idle: 0
# 连接池中的最大空闲连接
max-idle: 8
# 连接池的最大数据库连接数
max-active: 8
# #连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
---
j2cache:
openSpringCache: true
# 缓存中不存在时,运行缓存空对象
allowNullValues: true
redisClient: lettuce
l2CacheOpen: true
# 一级缓存使用caffeine
L1:
provider_class: caffeine
L2:
#使用springRedis替换二级缓存
provider_class: net.oschina.j2cache.cache.support.redis.SpringRedisProvider
config_section: redis
#使用springRedis进行广播通知缓失效
broadcast: net.oschina.j2cache.cache.support.redis.SpringRedisPubSubPolicy
# 上面配置的一级缓存为caffeine, 那么这里对一级缓存的配置就必须以这个caffeine开头
caffeine:
# 配置一级,二级缓存的region,有效时间
region.xj: 10000, 120s
---
spring:
cache:
# 一级缓存使用caffeine
type: caffeine
2.3 核心操作类
@Service
public class UserService {
private final UserRepository userRepository ;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository ;
}
@Transactional
public User save(User user) {
return this.userRepository.saveAndFlush(user) ;
}
@Cacheable(value = {"xj"}, key = "#id")
public User get(Long id) {
return this.userRepository.findById(id).orElse(null) ;
}
@Transactional
@CacheEvict(value = {"xj"}, key = "#id")
public void remove(Long id) {
this.userRepository.deleteById(id) ;
}
}
以上是基本的操作,非常简单。
2.4 Controller接口
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService ;
// 通过CacheChannel操作j2cache缓存方法
private final CacheChannel cacheChannel;
public UserController(UserService userService, CacheChannel cacheChannel) {
this.userService = userService ;
this.cacheChannel = cacheChannel ;
}
@GetMapping("/save")
public User save() {
User user = new User() ;
int num = new Random().nextInt(80);
user.setAge(num) ;
user.setName("姓名 - " + num) ;
user.setSex(num >= 50 ? "男" : "女") ;
return this.userService.save(user) ;
}
@GetMapping("/{id}")
public Object get(@PathVariable("id") Long id) {
// 从指定的region,指定的key获取数据,如果一级,二级缓存中不存在,则通过第三个参数Function手动获取
// 如果缓存中不存在时,同时配置了允许缓存空对象,则会缓存一个空对象到缓存中
return this.cacheChannel.get("xj", id.toString(), key -> this.userService.get(id) , true) ;
}
@GetMapping("/delete/{id}")
public Object remove(@PathVariable("id") Long id) {
this.userService.remove(id) ;
return "success" ;
}
}
2.5 测试
先通过save接口添加数据
图片
查询id=2的数据
图片
level=3 表示本次数据缓存中不存在,从数据库中获取的。刷新页面
图片
level=2,本次数据从二级缓存redis中获取。再次刷新页面
图片
level=1,本次数据从一级缓存caffeine中获取。后续再怎么刷新只要缓存没有过期都将从一级缓存中获取。
测试不存在的数据
图片
从数据库中查询不存在的数据。
图片
缓存了空对象。
测试删除数据
图片
缓存中会立即清除
图片
以上是本篇文章的全部内容,希望对你有帮助。
完毕!!!