前言

JetCache是阿里推出的一套替代springcache的缓存方案。JetCache是对SpringCache进行了封装,在原有基础上实现了多级缓存、缓存统计、自动刷新、异步调用、数据报表等功能。

JetCache设定了本地缓存与远程缓存的多级缓存方案

  • 本地缓存
    LinkedHashMap
    Caffeine
  • 远程缓存
    Redis
    Tair
    本地缓存和远程缓存可以任意组合

jetcache官方源码: https://github.com/alibaba/jetcache

注解

@EnableMethodCache

​ 表示支持方法注解缓存,相当于允许JetCache自动配置

属性默认值说明
basePackages未定义自动配置扫描的包,来进行Cache注解的扫描
modeproxy代理模式 支持两种 proxy代理 aspect 切面
proxyTargetClassfalse是否使用cglib子类进行代理,这会影响spring 管理的所有的bean 只会在mode为proxy生效
@Cached

添加缓存

属性默认值说明
areadefault缓存分类,相当于命名空间
name未定义指定缓存实例的名字
enabledtrue是否缓存
timeUnitTimeUnit.SECONDS过期时间单位,默认为秒
expire未定义过期时间
localExpire本地缓存当为多级缓存时 该时间表示本地缓存过期时间
cacheTypeCacheType.REMOTE缓存类型 remote 远程缓存 local 本地缓存 both 多级缓存
localLimit未定义本地缓存限制数量 当cacheType为local,both时生效,若不配置 会以全局的配置生效
serialPolicy未定义指定value的序列化方式 使用方式bean:beanName 在spring 容器中管理的名称
keyConvertor未定义指定key的序列化方式
key未定义指定缓存对应的key 支持spel表达式
cacheNullValuefalse是否缓存空值
condition未定义使用SpEL指定条件,如果表达式返回true的时候才去缓存中查询
postCondition未定义使用SpEL指定条件,如果表达式返回true的时候才更新缓存,该评估在方法执行后进行
@CacheUpdate

​ 更新缓存

属性默认值说明
areadefault缓存分类,相当于命名空间
name未定义指定缓存实例的名字
key未定义指定缓存对应的key 支持spel表达式
value未定义缓存的值 支持spel表达式
multifalse如果根据SpEL指定key和value都是集合并且元素的个数相同,则是否更新缓存实例中的对应的每个元素。如果设置为true,但是key不是集合或者value不是集合或者它们的元素的个数不相同,也不会更新缓存。
condition未定义使用SpEL指定条件,如果表达式返回true的时候才更新缓存
@CacheInvalidate

​ 失效缓存

属性默认值说明
areadefault缓存分类,相当于命名空间
name未定义指定缓存实例的名字
key未定义指定缓存对应的key 支持spel表达式
value未定义缓存的值 支持spel表达式
multifalse如果根据SpEL指定key和value都是集合并且元素的个数相同,则是否失效缓存实例中的对应的每个元素。如果设置为true,但是key不是集合或者value不是集合或者它们的元素的个数不相同,也不会失效缓存。
condition未定义使用SpEL指定条件,如果表达式返回true的时候才失效缓存
@CacheRefresh

​ 刷新缓存

属性默认值说明
refresh未定义表示间隔多久刷新缓存
stopRefreshAfterLastAccess未定义多久没有访问,就停止刷新缓存
refreshLockTimeout未定义刷新实例进行加锁时长,控制并发刷新
timeUnitTimeUnit.SECONDS时间单位
@CachePenetrationProtect

​ 当缓存访问未命中的情况下,对并发进行的加载行为进行保护。 目前只支持当 前应用内的保护,即同一个JVM中同一个key只有一个线程去加载,其它线程等待结果。

属性默认值说明
valuetrue是否开启
timeout未定义等待超时时间
timeUnitTimeUnit.SECONDS时间单位
@EnableCreateCacheAnnotation

​ 开启是否支持通过注解创建一个Cache实例

@CreateCache

​ 创建Cache实例,可以通过编码的形式对Cache进行操作

属性默认值说明
areadefault缓存分类,相当于命名空间
name未定义指定缓存实例的名字
timeUnitTimeUnit.SECONDS过期时间单位,默认为秒
expire未定义过期时间
localExpire本地缓存当为多级缓存时 该时间表示本地缓存过期时间
cacheTypeCacheType.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.statIntervalMinutes0统计间隔,0表示不统计
jetcache.areaInCacheNametrue(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}.keyConvertorfastjson2key转换器的全局配置,2.6.5+已经支持的keyConvertor:fastjson2/jackson; 2.6.5-只有一个已经实现的keyConvertor:fastjson。仅当使用@CreateCache且缓存类型为LOCAL时可以指定为none,此时通过equals方法来识别key。方法缓存必须指定keyConvertor
jetcache.[local/remote].${area}.valueEncoderjava序列化器的全局配置。仅remote类型的缓存需要指定,2.7+可选java/kryo/kryo5;2.6-可选java/kryo
jetcache.[local/remote].${area}.valueDecoderjava序列化器的全局配置。仅remote类型的缓存需要指定,2.7+可选java/kryo/kryo5;2.6-可选java/kryo
jetcache.[local/remote].${area}.limit100每个缓存实例的最大元素的全局配置,仅local类型的缓存需要指定。注意是每个缓存实例的限制,而不是全部,比如这里指定100,然后用@CreateCache创建了两个缓存实例(并且注解上没有设置localLimit属性),那么每个缓存实例的限制都是100
jetcache.[local/remote].${area}.expireAfterWriteInMillis无穷大以毫秒为单位指定超时时间的全局配置(以前为defaultExpireInMillis)
jetcache.remote.${area}.broadcastChanneljetcahe2.7的两级缓存支持更新以后失效其他JVM中的local cache,但多个服务共用redis同一个channel可能会造成广播风暴,需要在这里指定channel,你可以决定多个不同的服务是否共用同一个channel。如果没有指定则不开启。
jetcache.local.${area}.expireAfterAccessInMillis0需要jetcache2.2以上,以毫秒为单位,指定多长时间没有访问,就让缓存失效,当前只有本地缓存支持。0表示不使用这个功能。

${area}表示分类(相当于命名空间)

redis作为远程缓存配置项
属性默认值说明
jetcache.remote.${area}.poolConfig.maxTotal8最大连接数
jetcache.remote.${area}.poolConfig.maxIdle8最大空闲连接
jetcache.remote.${area}.poolConfig.minIdle0最小连接
jetcache.remote.${area}.poolConfig.*未定义和commonpool参数一致
jetcache.remote.${area}.host未定义redis地址
jetcache.remote.${area}.port0端口
jetcache.remote.${area}.timeout2000连接超时时间
jetcache.remote.${area}.password未定义密码
jetcache.remote.${area}.database0对应的库
jetcache.remote.${area}.clientName未定义从节点
jetcache.remote.${area}.sslfalse是否开启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.jetcache</groupId>
                <artifactId>jetcache-starter-redis</artifactId>
                <version>2.5.16</version>
            </dependency>
    

    其他版本参考

    jetcache版本spring版本spring boot版本说明
    2.54.0.8.RELEASE~5.1.1.RELEASE1.1.9.RELEASE~2.0.5.RELEASE
    2.65.0.4.RELEASE~5.2.4.RELEASE2.0.0.RELEASE~2.2.5.RELEASEjetcache-redis依赖jedis3.1.0,spring-data(jedis,boot版本<=2.1.X)依赖jedis2.9.3,不能同时用
    2.75.2.4.RELEASE~5.3.232.2.5.RELEASE~2.7.5jetcahe-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);
    }
    
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐