使用JetCache构建多级缓存
多级缓存使用:
- 本地缓存使用Caffeine
- 分布式缓存使用Redis(Redisson也可以)
JetCache简介:
JetCache是一个基于Java的缓存系统封装,提供统一的API和注解来简化缓存的使用。 JetCache提供了比SpringCache更加强大的注解,可以原生的支持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客户端时)
- Spring Boot支持
要求
JetCache需要JDK1.8、Spring Framework4.0.8以上版本。Spring Boot为可选,需要1.1.9以上版本。如果不使用注解(仅使用jetcache-core),Spring Framework也是可选的,此时使用方式与Guava/Caffeine cache类似。
可依赖的jar包
- jetcache-anno-api:定义jetcache的注解和常量,不传递依赖。如果你想把Cached注解加到接口上,又不希望你的接口jar传递太多依赖,可以让接口jar依赖jetcache-anno-api。
- jetcache-core:核心api,完全通过编程来配置操作
Cache,不依赖Spring。两个内存中的缓存实现LinkedHashMapCache和CaffeineCache也由它提供。 - jetcache-anno:基于Spring提供@Cached和@CreateCache注解支持。
- jetcache-redis:使用jedis提供Redis支持。
- jetcache-redis-lettuce(需要JetCache2.3以上版本):使用lettuce提供Redis支持,实现了JetCache异步访问缓存的的接口。
- jetcache-starter-redis:Spring Boot方式的Starter,基于Jedis。
- jetcache-starter-redis-lettuce(需要JetCache2.3以上版本):Spring Boot方式的Starter,基于Lettuce。
创建缓存实例:
依赖:这里使用了jedis客户端连接redis,也可以使用lettuce客户端
▼xml复制代码<dependency> <groupId>com.alicp.jetcache</groupId> <artifactId>jetcache-starter-redis</artifactId> <version>${jetcache.latest.version}</version> </dependency>
使用lettuce的方式访问Redis:
▼xml复制代码<!-- 使用Lettuce访问Redis --> <dependency> <groupId>com.alicp.jetcache</groupId> <artifactId>jetcache-redis-lettuce</artifactId> <version>2.7.5</version> </dependency> <dependency> <groupId>com.alicp.jetcache</groupId> <artifactId>jetcache-starter-redis-lettuce</artifactId> <version>2.7.5</version> </dependency>
yml文件配置:
▼yml复制代码server: port: 8080 spring: application: name: JetCache redis: host: localhost port: 6379 password: 123456 lettuce: pool: min-idle: 0 # 连接池中的最小空闲连接 max-idle: 10 # 连接池中的最大空闲连接 max-active: 20 # 连接池的最大连接数 max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制) jetcache: statIntervalMinutes: 15 areaInCacheName: false local: default: type: caffeine keyConvertor: fastjson2 remote: default: type: redis keyConvertor: fastjson2 broadcastChannel: ${spring.application.name} keyPrefix: ${spring.application.name} valueEncoder: java valueDecoder: java defaultExpireInMillis: 5000 poolConfig: minIdle: 5 maxIdle: 20 maxTotal: 50 host: 127.0.0.1 port: 6379 password: 123456
注意:
如果valueEncoder和valueDecoder配置成了java,那么初始化到缓存中的数据就是以二进制形式存储的
改进措施:
在初始化的时候,设置valueEncoder和valueDecoder,yml文件中的这两个配置就可以去掉了
.valueEncoder(Fastjson2ValueEncoder.INSTANCE)
.valueDecoder(Fastjson2ValueDecoder.INSTANCE)
▼java复制代码@Autowired private CacheManager cacheManager; private Cache<String, User> userCache; @PostConstruct public void init() { QuickConfig quickConfig = QuickConfig.newBuilder(":userCache:") // 缓存的key .expire(Duration.ofHours(2)) // 设置缓存的过期时间 .cacheType(CacheType.BOTH) // 缓存的类型 BOTH 表示本地缓存和分布式缓存都创建 .localLimit(1000) // 本地缓存元素的个数限制,只对CacheType.Local和CacheType.BOTH 有效(因为只有这两个有本地缓存) .syncLocal(true) // 两级缓存的情况下,缓存更新时发消息让其他JVM实例中的缓存失效,需要配置broadcastChannel才生效 .valueEncoder(Fastjson2ValueEncoder.INSTANCE) //使用fastjosn2进行序列化编码 .valueDecoder(Fastjson2ValueDecoder.INSTANCE) //使用fastjosn2进行序列化解码 .build(); userCache = cacheManager.getOrCreateCache(quickConfig); }
效果:
再次优化:使用自定义序列化的方式解决乱码
自定义编码器:
▼java复制代码package com.cache; import com.alicp.jetcache.support.AbstractValueEncoder; import com.alicp.jetcache.support.CacheEncodeException; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONWriter; public class CustomFastjson2ValueEncoder extends AbstractValueEncoder { public static final CustomFastjson2ValueEncoder INSTANCE = new CustomFastjson2ValueEncoder(true); // 指定序列化特性 private static final JSONWriter.Feature[] features = { JSONWriter.Feature.WriteClassName, // 写入类名(可选,按需开启) //JSONWriter.Feature.DisableCircularReferenceDetect // 禁用循环引用检测 }; public CustomFastjson2ValueEncoder(boolean useIdentityNumber) { super(useIdentityNumber); } @Override public byte[] apply(Object value) { try { // 使用 UTF-8 编码序列化 return JSON.toJSONBytes(value, features); } catch (Exception e) { throw new CacheEncodeException("Fastjson2 Encode Error: " + e.getMessage(), e); } } }
自定义解码器:
▼java复制代码package com.cache; import com.alicp.jetcache.support.AbstractValueDecoder; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONReader; import com.alicp.jetcache.support.CacheEncodeException; public class CustomFastjson2ValueDecoder extends AbstractValueDecoder { public static final CustomFastjson2ValueDecoder INSTANCE = new CustomFastjson2ValueDecoder(true); // 指定反序列化特性 private static final JSONReader.Feature[] features = { //JSONReader.Feature.SupportAutoType, // 启用 AutoType(需白名单配置) JSONReader.Feature.IgnoreAutoTypeNotMatch }; public CustomFastjson2ValueDecoder(boolean useIdentityNumber) { super(useIdentityNumber); } @Override public Object doApply(byte[] bytes) { try { return JSON.parseObject(bytes, Object.class, features); } catch (Exception e) { throw new CacheEncodeException("Fastjson2 Decode Error: " + e.getMessage(), e); } } }
效果:

评论
问答助学
相关内容
0个评论
全部评论
点击登录,快来和大家讨论吧~
表情
图片
暂无评论
内容推荐
AI应用开发中的流式输出:从协议原理到工程实战的完整指南
38
第1章 LearnWise AI项目介绍
5
Spring Boot 如何处理跨域请求(CORS):深度调研报告
3
【入职求助贴】萌新刚入职某大厂做后端开发,目前还在试用期。最近遇到一个棘手的问题,想向大家求助一下。入职不久,leader 给我派了一个任务。跟我说是0.5天就可以解决,我刚毕业入职,做了一个星期没有做出来。实现一个收集定时成功任务的案例。听起来好像不复杂,但我自己摸索着做了一整个星期,到现在还没达到预期效果。这一周我基本是“边学边做”的状态,遇到卡点也会每天主动找 leader 沟通进度和疑问。
2
#字节内推# 实习、校招、社招均有岗位
4
