在实际应用中,并不是单一的使用本地缓存或者redis,更多是组合使用来满足不同的业务场景,于是如何优雅的组合本地缓存和远程缓存就成了我们要研究的问题,而这一点,阿里开源的
jetcache
组件帮我们实现了
jetcache
是阿里开源的基于java开发的缓存框架,支持多种缓存类型:本地缓存、分布式缓存、多级缓存。能够满足不同业务场景的缓存需求。
jetcache具有上手简单、性能高效、拓展性强的特点。支持缓存预热 、缓存key前缀等功能。结合spring-cache使用,可以实现十分优雅的缓存类型切换
官网地址:https://github.com/alibaba/jetcache
官方文档:https://github.com/alibaba/jetcache/tree/master/docs/CN
基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://github.com/YunaiV/ruoyi-vue-pro
视频教程:https://doc.iocoder.cn/video/
1、引入依赖,这里我们使用sringboot项目框架,同时使用redis作为远程缓存。于是我们引入
jetcache-starter-redis
依赖,这里我的springboot版本为
2.6.13
如果是非springboot项目可以参考官网说明配置
<dependency > <groupId > com.alicp.jetcachegroupId > <artifactId > jetcache-starter-redisartifactId > <version > 2.7.0version >dependency ><dependency > <groupId > redis.clientsgroupId > <artifactId > jedisartifactId > <version > 4.3.1version >dependency >
对应的版本说明如下:https://github.com/alibaba/jetcache/blob/master/docs/CN/Compatibility.md
2、修改配置文件,配置redis地址和线程数
jetcache: # 统计间隔,0表示不统计,开启后定期在控制台输出缓存信息 statIntervalMinutes: 15 # 是否把cacheName作为远程缓存key前缀 areaInCacheName: false # 本地缓存配置 local: default: # default表示全部生效,也可以指定某个cacheName # 本地缓存类型,其他可选:caffeine/linkedhashmap type: linkedhashmap keyConvertor: fastjson # 远程缓存配置 remote: default: # default表示全部生效,也可以指定某个cacheName type: redis # key转换器方式n keyConvertor: fastjson broadcastChannel: projectA # redis序列化方式 valueEncoder: java valueDecoder: java # redis线程池 poolConfig: minIdle: 5 maxIdle: 20 maxTotal: 50 # redis地址与端口 host: 127.0 .0 .1 port: 6379
更详细的参数配置可参考官网说明:
3、启动类添加注解
@EnableCreateCacheAnnotation
,开启缓存,添加
@EnableMethodCache(basePackages = "com.example.jetcachedemo")
注解,配置缓存方法扫描路径
4、使用缓存可以通过三种方式:
方式一(推荐)AOP模式:通过
@Cached
,
@CacheUpdate
,
@CacheInvalidate
注解
@RestController @RequestMapping ("user" )public class UserController { @GetMapping ("getRemote" ) @Cached (name="userCache:" , key = "#id" , expire = 3600 , timeUnit = TimeUnit.SECONDS, cacheType = CacheType.REMOTE) public User getRemote (Long id) { // 直接新建用户,模拟从数据库获取数据 User user = new User(); user.setId(id); user.setName("用户remote" +id); user.setAge(23 ); user.setSex(1 ); System.out.println("第一次获取数据,未走缓存:" +id); return user; } @GetMapping ("getLocal" ) @Cached (name="userCache:" , key = "#id" , expire = 3600 , timeUnit = TimeUnit.SECONDS, cacheType = CacheType.LOCAL) public User getLocal (Long id) { // 直接新建用户,模拟从数据库获取数据 User user = new User(); user.setId(id); user.setName("用户local" +id); user.setAge(23 ); user.setSex(1 ); System.out.println("第一次获取数据,未走缓存:" +id); return user; } @GetMapping ("getBoth" ) @Cached (name="userCache:" , key = "#id" , expire = 3600 , timeUnit = TimeUnit.SECONDS, cacheType = CacheType.BOTH) public User getBoth (Long id) { // 直接新建用户,模拟从数据库获取数据 User user = new User(); user.setId(id); user.setName("用户both" +id); user.setAge(23 ); user.setSex(1 ); System.out.println("第一次获取数据,未走缓存:" +id); return
user; } @PostMapping ("updateUser" ) @CacheUpdate (name = "userCache:" , key = "#user.id" , value = "#user" ) public Boolean updateUser (@RequestBody User user) { // TODO 更新数据库 return true ; } @PostMapping ("deleteUser" ) @CacheInvalidate (name = "userCache:" , key = "#id" ) public Boolean deleteUser (Long id) { // TODO 从数据库删除 return true ; } }
这里要注意实体类
User
一定要实现序列化,即声明
Serializable
@Data public class User implements Serializable { private Long id; private String name; private Integer age; private Integer sex; }
方式二 API模式:通过
@CreateCache
,注:在jetcache 2.7 版本CreateCache注解已废弃,不推荐使用
@RestController @RequestMapping ("user2" )public class User2Controller { @CreateCache (name= "userCache:" , expire = 3600 , timeUnit = TimeUnit.SECONDS, cacheType = CacheType.BOTH) private Cache userCache; @GetMapping ("get" ) public User get (Long id) { if (userCache.get(id) != null ){ return (User) userCache.get(id); } User user = new User(); user.setId(id); user.setName("用户both" +id); user.setAge(23 ); user.setSex(1 ); userCache.put(id, user); System.out.println("第一次获取数据,未走缓存:" +id); return user; } @PostMapping ("updateUser" ) public Boolean updateUser (@RequestBody User user) { // TODO 更新数据库 userCache.put(user.getId(), user); return true ; } @PostMapping ("deleteUser" ) public Boolean deleteUser (Long id) { // TODO 从数据库删除 userCache.remove(id); return true ; } }
方式三 高级API模式:通过
CacheManager
,2.7 版本才可使用
(1)添加依赖
<dependency > <groupId > com.alibabagroupId > <artifactId > fastjsonartifactId > <version > 2.0.25version >dependency >
(2)书写配置类
@Configuration public class JetcacheConfig { @Autowired private CacheManager cacheManager; private Cache 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 getUserCache () { return userCache; } }
(3)调用代码
@RestController @RequestMapping ("user3" )public class User3Controller { @Autowired JetcacheConfig jetcacheConfig; @Autowired private Cache userCache; @GetMapping ("get" ) public User get (Long id) { if (userCache.get(id) != null ){ return (User) userCache.get(id); } User user = new User(); user.setId(id); user.setName("用户both" +id); user.setAge(23 ); user.setSex(1 ); userCache.put(id, user); System.out.println("第一次获取数据,未走缓存:" +id); return user; } @PostMapping ("updateUser" ) public Boolean updateUser (@RequestBody User user) { // TODO 更新数据库 userCache.put(user.getId(), user); return true ; } @PostMapping ("deleteUser" ) public Boolean deleteUser (Long id) { // TODO 从数据库删除 userCache.remove(id); return true ; } }
多级缓存的形式,会先从本地缓存获取数据,本地获取不到会从远程缓存获取;关注工众号:码猿技术专栏,回复关键词:1111 获取阿里内部Java性能调优手册!
5、启动redis,启动演示项目
注意,如果启动出现
NoClassDefFoundError: redis/clients/util/Pool
或
NoClassDefFoundError: redis/clients/jedis/UnifiedJedis
报错,说明springboot与jetcache版本不一致,对应关系可参考上述第一步中的说明 同时如果使用的是jetcache2.7.x版本,因为该版本中有jedis包的依赖,需要额外添加如下依赖,或者将jetcache版本将至2.6.5以下
<dependency > <groupId > redis.clientsgroupId > <artifactId > jedisartifactId > <version > 4.3.1version >dependency >
基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://github.com/YunaiV/yudao-cloud
视频教程:https://doc.iocoder.cn/video/
1、访问localhost:8088/user/getRemote?id=1
因为配置的是远程缓存,在redis中也能看到对应的key
2、访问localhost:8088/user/getLocal?id=1,这个方法是从本地缓存获取的,现在只有远程缓存上有数据,我们调用发现缓存数据还是拿到了,这说明当我们在配置文件中配置了本地缓存和远程缓存后,方式一中本地缓存和远程缓存会自动相互调用