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

  1. 实现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);
        }
    }
    
  2. 把自定义的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;
        }
    
  3. 注解中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

  1. 引入依赖 2.5.16 对应springboot版本为2.1.5

           <dependency>
                <groupId>com.alicp.jetcachegroupId>
                <artifactId>jetcache-starter-redisartifactId>
                <version>2.5.16version>
            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,不能同时用
  2. 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);
        }
    
    }
    
    
  3. 配置

    # 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库
    
  4. 注解使用

       
        /**
        * 缓存查询结果
        */
        @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);
        }
    
  5. 使用@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;
        }
    
  6. 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);
    }
    

你可能感兴趣的:(缓存,redis,java)