JetCache多级缓存看这篇文章就够了
前言
JetCache是阿里推出的一套替代springcache的缓存方案。JetCache是对SpringCache进行了封装,在原有基础上实现了多级缓存、缓存统计、自动刷新、异步调用、数据报表等功能。
JetCache设定了本地缓存与远程缓存的多级缓存方案
- 本地缓存
LinkedHashMap
Caffeine - 远程缓存
Redis
Tair
本地缓存和远程缓存可以任意组合
jetcache官方源码: https://github.com/alibaba/jetcache
注解
@EnableMethodCache
表示支持方法注解缓存,相当于允许JetCache自动配置
属性 | 默认值 | 说明 |
---|---|---|
basePackages | 未定义 | 自动配置扫描的包,来进行Cache注解的扫描 |
mode | proxy | 代理模式 支持两种 proxy代理 aspect 切面 |
proxyTargetClass | false | 是否使用cglib子类进行代理,这会影响spring 管理的所有的bean 只会在mode为proxy生效 |
@Cached
添加缓存
属性 | 默认值 | 说明 |
---|---|---|
area | default | 缓存分类,相当于命名空间 |
name | 未定义 | 指定缓存实例的名字 |
enabled | true | 是否缓存 |
timeUnit | TimeUnit.SECONDS | 过期时间单位,默认为秒 |
expire | 未定义 | 过期时间 |
localExpire | 本地缓存 | 当为多级缓存时 该时间表示本地缓存过期时间 |
cacheType | CacheType.REMOTE | 缓存类型 remote 远程缓存 local 本地缓存 both 多级缓存 |
localLimit | 未定义 | 本地缓存限制数量 当cacheType为local,both时生效,若不配置 会以全局的配置生效 |
serialPolicy | 未定义 | 指定value的序列化方式 使用方式bean:beanName 在spring 容器中管理的名称 |
keyConvertor | 未定义 | 指定key的序列化方式 |
key | 未定义 | 指定缓存对应的key 支持spel表达式 |
cacheNullValue | false | 是否缓存空值 |
condition | 未定义 | 使用SpEL指定条件,如果表达式返回true的时候才去缓存中查询 |
postCondition | 未定义 | 使用SpEL指定条件,如果表达式返回true的时候才更新缓存,该评估在方法执行后进行 |
@CacheUpdate
更新缓存
属性 | 默认值 | 说明 |
---|---|---|
area | default | 缓存分类,相当于命名空间 |
name | 未定义 | 指定缓存实例的名字 |
key | 未定义 | 指定缓存对应的key 支持spel表达式 |
value | 未定义 | 缓存的值 支持spel表达式 |
multi | false | 如果根据SpEL指定key和value都是集合并且元素的个数相同,则是否更新缓存实例中的对应的每个元素。如果设置为true,但是key不是集合或者value不是集合或者它们的元素的个数不相同,也不会更新缓存。 |
condition | 未定义 | 使用SpEL指定条件,如果表达式返回true的时候才更新缓存 |
@CacheInvalidate
失效缓存
属性 | 默认值 | 说明 |
---|---|---|
area | default | 缓存分类,相当于命名空间 |
name | 未定义 | 指定缓存实例的名字 |
key | 未定义 | 指定缓存对应的key 支持spel表达式 |
value | 未定义 | 缓存的值 支持spel表达式 |
multi | false | 如果根据SpEL指定key和value都是集合并且元素的个数相同,则是否失效缓存实例中的对应的每个元素。如果设置为true,但是key不是集合或者value不是集合或者它们的元素的个数不相同,也不会失效缓存。 |
condition | 未定义 | 使用SpEL指定条件,如果表达式返回true的时候才失效缓存 |
@CacheRefresh
刷新缓存
属性 | 默认值 | 说明 |
---|---|---|
refresh | 未定义 | 表示间隔多久刷新缓存 |
stopRefreshAfterLastAccess | 未定义 | 多久没有访问,就停止刷新缓存 |
refreshLockTimeout | 未定义 | 刷新实例进行加锁时长,控制并发刷新 |
timeUnit | TimeUnit.SECONDS | 时间单位 |
@CachePenetrationProtect
当缓存访问未命中的情况下,对并发进行的加载行为进行保护。 目前只支持当 前应用内的保护,即同一个JVM中同一个key只有一个线程去加载,其它线程等待结果。
属性 | 默认值 | 说明 |
---|---|---|
value | true | 是否开启 |
timeout | 未定义 | 等待超时时间 |
timeUnit | TimeUnit.SECONDS | 时间单位 |
@EnableCreateCacheAnnotation
开启是否支持通过注解创建一个Cache实例
@CreateCache
创建Cache实例,可以通过编码的形式对Cache进行操作
属性 | 默认值 | 说明 |
---|---|---|
area | default | 缓存分类,相当于命名空间 |
name | 未定义 | 指定缓存实例的名字 |
timeUnit | TimeUnit.SECONDS | 过期时间单位,默认为秒 |
expire | 未定义 | 过期时间 |
localExpire | 本地缓存 | 当为多级缓存时 该时间表示本地缓存过期时间 |
cacheType | CacheType.REMOTE | 缓存类型 remote 远程缓存 local 本地缓存 both 多级缓存 |
localLimit | 未定义 | 本地缓存限制数量 当cacheType为local,both时生效,若不配置 会以全局的配置生效 |
serialPolicy | 未定义 | 指定value的序列化方式 使用方式bean:beanName 在spring 容器中管理的名称 |
keyConvertor | 未定义 | 指定key的序列化方式 |
自定义value序列化
可以实现接口SerialPolicy,实现里面两种方法 encoder和decoder,下面是使用redis作为远程缓存,自定义实现jackson来进行序列化value
-
实现SerialPolicy接口
package com.jx.shop.config.cache.local; import com.alibaba.fastjson.JSON; import com.alicp.jetcache.anno.SerialPolicy; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; import java.util.function.Function; /** * @author tangchen * @date 2023/6/14 14:12 * @copyright 2023 barm Inc. All rights reserved */ public class JxJsonSerialPolicy implements SerialPolicy { private Jackson2JsonRedisSerializer<CacheValueHolder> jackson2JsonRedisSerializer; public void setJackson2JsonRedisSerializer(Jackson2JsonRedisSerializer<CacheValueHolder> jackson2JsonRedisSerializer) { this.jackson2JsonRedisSerializer = jackson2JsonRedisSerializer; } @Override public Function<Object, byte[]> encoder() { return (value) -> jackson2JsonRedisSerializer.serialize(value); } @Override public Function<byte[], Object> decoder() { return bytes -> jackson2JsonRedisSerializer.deserialize(bytes); } }
-
把自定义的SerialPolicy配置到spring容器中
@Bean(name = "cacheJackson2") JxJsonSerialPolicy jxJsonSerialPolicy() { Jackson2JsonRedisSerializer<CacheValueHolder> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(CacheValueHolder.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); JxJsonSerialPolicy serialPolicy = new JxJsonSerialPolicy(); serialPolicy.setJackson2JsonRedisSerializer(jackson2JsonRedisSerializer); return serialPolicy; }
-
注解中serialPolicy设置为bean:cacheJackson2 或者全局配置jetcache.remote.default.valueEncoder=bean:cacheJackson2 和 jetcache.remote.default.valueDecoder=bean:cacheJackson2
配置项
全局配置
属性 | 默认值 | 说明 |
---|---|---|
jetcache.statIntervalMinutes | 0 | 统计间隔,0表示不统计 |
jetcache.areaInCacheName | true(2.6-) false(2.7+) | jetcache-anno把cacheName作为远程缓存key前缀,2.4.3以前的版本总是把areaName加在cacheName中,因此areaName也出现在key前缀中。2.4.4以后可以配置,为了保持远程key兼容默认值为true,但是新项目的话false更合理些,2.7默认值已改为false。 |
jetcache.hiddenPackages | 无 | @Cached和@CreateCache自动生成name的时候,为了不让name太长,hiddenPackages指定的包名前缀被截掉 |
jetcache.[local/remote].${area}.type | 无 | 缓存类型。tair、redis为当前支持的远程缓存;linkedhashmap、caffeine为当前支持的本地缓存类型 |
jetcache.[local/remote].${area}.keyConvertor | fastjson2 | key转换器的全局配置,2.6.5+已经支持的keyConvertor:fastjson2 /jackson ; 2.6.5-只有一个已经实现的keyConvertor:fastjson 。仅当使用@CreateCache且缓存类型为LOCAL时可以指定为none ,此时通过equals方法来识别key。方法缓存必须指定keyConvertor |
jetcache.[local/remote].${area}.valueEncoder | java | 序列化器的全局配置。仅remote类型的缓存需要指定,2.7+可选java /kryo /kryo5 ;2.6-可选java /kryo |
jetcache.[local/remote].${area}.valueDecoder | java | 序列化器的全局配置。仅remote类型的缓存需要指定,2.7+可选java /kryo /kryo5 ;2.6-可选java /kryo |
jetcache.[local/remote].${area}.limit | 100 | 每个缓存实例的最大元素的全局配置,仅local类型的缓存需要指定。注意是每个缓存实例的限制,而不是全部,比如这里指定100,然后用@CreateCache创建了两个缓存实例(并且注解上没有设置localLimit属性),那么每个缓存实例的限制都是100 |
jetcache.[local/remote].${area}.expireAfterWriteInMillis | 无穷大 | 以毫秒为单位指定超时时间的全局配置(以前为defaultExpireInMillis) |
jetcache.remote.${area}.broadcastChannel | 无 | jetcahe2.7的两级缓存支持更新以后失效其他JVM中的local cache,但多个服务共用redis同一个channel可能会造成广播风暴,需要在这里指定channel,你可以决定多个不同的服务是否共用同一个channel。如果没有指定则不开启。 |
jetcache.local.${area}.expireAfterAccessInMillis | 0 | 需要jetcache2.2以上,以毫秒为单位,指定多长时间没有访问,就让缓存失效,当前只有本地缓存支持。0表示不使用这个功能。 |
${area}表示分类(相当于命名空间)
redis作为远程缓存配置项
属性 | 默认值 | 说明 |
---|---|---|
jetcache.remote.${area}.poolConfig.maxTotal | 8 | 最大连接数 |
jetcache.remote.${area}.poolConfig.maxIdle | 8 | 最大空闲连接 |
jetcache.remote.${area}.poolConfig.minIdle | 0 | 最小连接 |
jetcache.remote.${area}.poolConfig.* | 未定义 | 和commonpool参数一致 |
jetcache.remote.${area}.host | 未定义 | redis地址 |
jetcache.remote.${area}.port | 0 | 端口 |
jetcache.remote.${area}.timeout | 2000 | 连接超时时间 |
jetcache.remote.${area}.password | 未定义 | 密码 |
jetcache.remote.${area}.database | 0 | 对应的库 |
jetcache.remote.${area}.clientName | 未定义 | 从节点 |
jetcache.remote.${area}.ssl | false | 是否开启ssl |
jetcache.remote.${area}.masterName | 未定义 | 主节点 |
jetcache.remote.${area}.sentinels | 未定义 | 哨兵模式 ip1:port,ip2:port |
Demo
-
引入依赖 2.5.16 对应springboot版本为2.1.5
<dependency> <groupId>com.alicp.jetcache</groupId> <artifactId>jetcache-starter-redis</artifactId> <version>2.5.16</version> </dependency>
其他版本参考
jetcache版本 spring版本 spring boot版本 说明 2.5 4.0.8.RELEASE~5.1.1.RELEASE 1.1.9.RELEASE~2.0.5.RELEASE 2.6 5.0.4.RELEASE~5.2.4.RELEASE 2.0.0.RELEASE~2.2.5.RELEASE jetcache-redis依赖jedis3.1.0,spring-data(jedis,boot版本<=2.1.X)依赖jedis2.9.3,不能同时用 2.7 5.2.4.RELEASE~5.3.23 2.2.5.RELEASE~2.7.5 jetcahe-redis依赖jedis4,spring-data(jedis)依赖jedis3,不能同时用 -
Spring Boot启动类
启动类上新增注解@EnableMethodCache(basePackages = “com.jx.shop”)以及@EnableCreateCacheAnnotation
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients(basePackages = {"com.jx.sdk"}) @EnableScheduling @EnableRocket @EnableCaching @EnableAsync @EnableAspectJAutoProxy(exposeProxy = true,proxyTargetClass = true) @EnableMethodCache(basePackages = "com.jx.shop") //basePackages 指定扫描的包 @EnableCreateCacheAnnotation //开启通过注解创建Cache实例 public class ShopCoreApplication { public static void main(String[] args) { SpringApplication.run(ShopCoreApplication.class, args); } }
-
配置
# jetCache配置 jetcache: # 是否统计 statIntervalMinutes: 0 # cache名字是否加上area 若设置为true 则缓存的key为 area_cachenam${key} 例如 area为default cachename为 user:info key为1 则缓存的key的值为:default_user:info1 areaInCacheName: false # 一级缓存 local: default: # 可以使用 caffeine 或者 linkedhashmap type: caffeine # 序列化方式 keyConvertor: fastjson limit: 100 # 本地缓存限制个数 remote: default: # 需要和注解上面的area保持一致 表示这个area的远程缓存配置 type: redis # 远程缓存了类型 keyConvertor: fastjson # key序列化方式 valueEncoder: bean:cacheJackson2 valueDecoder: bean:cacheJackson2 # value 反序列化 目前支持 java和kryo poolConfig: minIdle: 5 #最小空闲连接数 maxIdle: 20 # 最大空闲连接数 maxTotal: 50 # 最大连接数 host: 0.0.0.0 # 指定自己的redis地址 port: 6379 # 指定自己的port password: xxxx #若redis设置了密码 需配置自己的密码 database: 0 # redis库
-
注解使用
/** * 缓存查询结果 */ @PostMapping("/test-cache/admin") @Cached(name = "test:cache", cacheType = CacheType.BOTH, localLimit = 1, key = "#userId", localExpire = 30, expire = 600) public CommonResult<Integer> testCache(Integer userId) { log.info("没有走缓存-----------userId:{}", userId); return CommonResult.success(userId); } /** * 失效缓存 */ @PostMapping("/test-cache-expire/admin") @CacheInvalidate(name = "test:cache", key = "#userId") public CommonResult<Integer> testCacheExpire(Integer userId) { log.info("没有走缓存-----------userId:{}", userId); return CommonResult.success(userId); }
-
使用@CreateCache注解来创建cache实例
//增加成员属性 当area和name和其他注解上一致时 缓存是同一实例 比如先调用上面的 test-cache/admin接口 然后访问/create-cache/admin接口 相同参数 可以直接取出缓存的值 @CreateCache(name = "test:cache", expire = 600, localExpire = 30, cacheType = CacheType.BOTH) private Cache<Integer, CommonResult<Integer>> testCache; //使用 @PostMapping("/create-cache/admin") public CommonResult<Integer> createCache(Integer userId) { CommonResult<Integer> result = testCache.get(userId); return result; }
-
api
//当key对应的缓存不存在时,使用loader加载。通过这种方式,loader的加载时间可以被统计到。 V computeIfAbsent(K key, Function<K, V> loader) //比上面那个方法新增参数cacheNullWhenLoaderReturnNull 是否缓存loader返回值为空的结果 V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull) //增加过期时间的配置 若使用上面两个方法 过期时间是@CreateCache配置的时间 V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull, long expire, TimeUnit timeUnit) //put操作 缓存的超时时间是@CreateCache配置的时间 void put(K key, V value) //put操作,expire和timeUnit指定了缓存的超时时间,会覆盖缓存的默认超时时间。 void put(K key, V value, long expire, TimeUnit timeUnit) //获取锁 AutoReleaseLock tryLock(K key, long expire, TimeUnit timeUnit) //锁获取成功后 需要执行的方法 boolean tryLockAndRun(K key, long expire, TimeUnit timeUnit, Runnable action)
除此之外,还提供了一些方法 名字是大写的方法,相比上面的方法,提供了完整的返回值,使用上比较繁琐一些。
CacheGetResult<OrderDO> r = cache.GET(orderId); if( r.isSuccess() ){ OrderDO order = r.getValue(); } else if (r.getResultCode() == CacheResultCode.NOT_EXISTS) { System.out.println("cache miss:" + orderId); } else if(r.getResultCode() == CacheResultCode.EXPIRED) { System.out.println("cache expired:" + orderId)); } else { System.out.println("cache get error:" + orderId); }
更多推荐
所有评论(0)