• JetCache-的使用(入门教程)



    前言

    我不生产知识,我只是知识的搬运工,以下是我使用 spring-boot 整合 jetCache 的一些经验分享。


    一、概述

    githubhttps://github.com/alibaba/jetcache

    官方文档:https://github.com/alibaba/jetcache/wiki/Home_CN

    官网上是这么描述 JetCache的:

    JetCache是一个基于Java的缓存系统封装,提供统一的API和注解来简化缓存的使用。 JetCache提供了比SpringCache更加强大的注解,可以原生的支持TTL、两级缓存、分布式自动刷新,还提供了 Cache 接口用于手工缓存操作。 当前有四个实现,RedisCacheTairCache(此部分未在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 对 SpringCache 进行了封装,所以 JetCache 的使用和 SpringCache 的使用也是非常的相似。并且 jetCache 在原有功能基础上实现了多级缓存、缓存统计、自动刷星、异步调用、数据报表等功能。

    jetCache 设定了本地缓存与远程缓存的多级缓存解决方案

    • 本地缓存(local)
      • linkedHashMap
      • Caffeine
    • 远程缓存(remote)
      • Redis
      • Tair

    本地缓存一般使用的是 linkedHashMap,远程缓存一般使用的是 Redis

    功能简述:

    1. 有接口与 api 两种缓存实现,接口使用方便,api 使用类似于 map,可以更细粒度的控制缓存
      - @CreateCache
      - @Cached
    2. 可选择内存缓存、分布式缓存,或者同时存在,同时存在时优先访问内存
    3. 自动刷新策略,防止某个缓存失效,突然访问量增大,导致数据库挂掉(缓存雪崩)
    4. 有不严格的分布式锁,对同一 key,全局只有一台机器自动刷新

    二、使用步骤

    1. 引入依赖坐标

    可以去 maven 仓库 去寻找 jetCache 所对应的坐标,直接搜索 jetcache ,就能找到

    在这里插入图片描述

    一般我们选择 JetCache Starter Redis 这个坐标。

    在这里插入图片描述

    点击进入,选择 和自己项目不冲突 的版本。(PS:这个我也不好说,我是一个个试出来的)

    在这里插入图片描述

    复制坐标,粘到项目之中即可

    在这里插入图片描述

    <!-- https://mvnrepository.com/artifact/com.alicp.jetcache/jetcache-starter-redis -->
    <dependency>
        <groupId>com.alicp.jetcache</groupId>
        <artifactId>jetcache-starter-redis</artifactId>
        <version>2.6.0</version>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2. 编写配置文件

    上面说过 JetCache 不仅支持本地本地缓存,也支持远程缓存。

    yml 配置如下:(可参考官方配置说明

    # jetCache 配置
    jetcache:
      # 指定统计间隔,以分钟为单位,默认0:表示不统计
      statIntervalMinutes: 15
      # areaName 是否作为缓存 key 前缀,默认 True
      areaInCacheName: false
      # 本地缓存方案
      local:
        # 区域 area 名
        default:
          # 缓存类型,已支持可选:linkedhashmap、caffeine
          type: linkedhashmap
          # key 转换器的全局配置,当前只有:fastjson
          keyConvertor: fastjson
          # 每个缓存实例的最大元素的全局配置,仅 local 类型的缓存需要指定
          limit: 100
          # jetCache2.2 以上,以毫秒为单位,指定多长时间没有访问,就让缓存失效,当前只有本地缓存支持。0表示不使用这个功能
          expireAfterAccessInMillis: 30000
      # 远程缓存方案
      remote:
        # 区域 area 名
        default:
          # 缓存类型,已支持可选:redis、tair
          type: redis
          # key 转换器的全局配置,当前只有:fastjson
          keyConvertor: fastjson
          # 序列化器的全局配置。仅remote类型的缓存需要指定,可选java和kryo
          valueEncoder: java
          valueDecoder: java
          # redis ip 地址
          host: 127.0.0.1
          # redis 端口号
          port: 6379
          # host 和 port 也可以用 url 配置:如下
          # uri: redis://localhost:6379/1?timeout=5s
          # 如果 redis 有设置密码需要加上 password
          # password:
          # 以毫秒为单位指定超时时间的全局配置
          expireAfterWriteInMillis: 5000
          # 集群模式
          # mode: MasterSlave # (主从模式)
          poolConfig:
            minIdle: 5
            maxIdle: 20
            maxTotal: 50
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    3. 注解开启缓存

    在启动类上使用 @EnableCreateCacheAnnotation 激活 @CreateCache,使用 @EnableMethodCache 激活 @Cached,同时 @EnableMethodCache 还需要指定扫描包的路径。

    在这里插入图片描述

    4. @CreateCach 的使用

    通过 @CreateCache 注解创建 Cache 实例,这种灵活性比较高。

    属性表:

    在Spring bean中使用@CreateCache注解创建一个Cache实例。例如:

    @CreateCache(expire = 100)
    private Cache<Long, UserDO> userCache;
    
    • 1
    • 2
    属性默认值说明
    area“default”如果需要连接多个缓存系统,可在配置多个cache area,这个属性指定要使用的那个area的name
    name未定义指定缓存的名称,不是必须的,如果没有指定,会使用类名+方法名。name会被用于远程缓存的key前缀。另外在统计中,一个简短有意义的名字会提高可读性。如果两个@CreateCachenamearea相同,它们会指向同一个Cache实例
    expire未定义该Cache实例的默认超时时间定义,注解上没有定义的时候会使用全局配置,如果此时全局配置也没有定义,则取无穷大
    timeUnitTimeUnit.SECONDS指定expire的单位
    cacheTypeCacheType.REMOTE缓存的类型,包括CacheType.REMOTECacheType.LOCALCacheType.BOTH。如果定义为BOTH,会使用LOCAL和REMOTE组合成两级缓存
    localLimit未定义如果cacheType为CacheType.LOCAL或CacheType.BOTH,这个参数指定本地缓存的最大元素数量,以控制内存占用。注解上没有定义的时候会使用全局配置,如果此时全局配置也没有定义,则取100
    serialPolicy未定义如果cacheType为CacheType.REMOTE或CacheType.BOTH,指定远程缓存的序列化方式。JetCache内置的可选值为SerialPolicy.JAVA和SerialPolicy.KRYO。注解上没有定义的时候会使用全局配置,如果此时全局配置也没有定义,则取SerialPolicy.JAVA
    keyConvertor未定义指定KEY的转换方式,用于将复杂的KEY类型转换为缓存实现可以接受的类型,JetCache内置的可选值为KeyConvertor.FASTJSON和KeyConvertor.NONE。NONE表示不转换,FASTJSON通过fastjson将复杂对象KEY转换成String。如果注解上没有定义,则使用全局配置。

    列举:我在我的 TestServiceImpl 类中定义了一个 jetCache 的缓存,并且用 @CreateCache 注解表示该对象是用于缓存的对象,代码如下:

        // 定义一个 jetCache 缓存,名称为 jetCache_,过期时间 3600 秒
        // timeUnit 默认为 TimeUnit.SECONDS,可换成其它时间单位
        // area 使用的是默认配置(可不写),如果想要使用其它缓存系统,必须指定名称
        @CreateCache(area = "default",name = "jetCache_",expire = 3600,timeUnit = TimeUnit.SECONDS)
        private Cache<String,String> jetCache;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    这里的缓存的 key 和 value 的类型可根据需要自定义,Cache 的导包使用 com.alicp.jetcahe

    在这里插入图片描述

    然后我在这个实现类中写了两个方法,一个往缓存中存值,一个往缓存中取值,分别对应 /put-cache/get-cache 两个接口,代码如下

    	@CreateCache(area = "default",name = "jetCache_",expire = 3600,timeUnit = TimeUnit.SECONDS)
        private Cache<String,String> jetCache;
    
        @Override
        public void putCache() {
            String key = "mike";
            jetCache.put(key,"随便存点东西");
        }
    
        @Override
        public void getCache() {
            String key = "mike";
            String value = jetCache.get(key);
            System.out.println("value = " + value);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    先调 /put-cache 存数据、再调 /get-cache 取数据,控制台输出如下:

    在这里插入图片描述

    可以看到通过 key 是能够获取之前存放的 value 的,用法和 map 集合类似,这样就实现了通过 JetCache 实现缓存。

    比较常用的方法就是putget 方法, Cache 接口的详细 API 可参考官方文档,这里就不多赘述了。


    5. 方法缓存的使用

    JetCache方法缓存和SpringCache比较类似,它原生提供了TTL支持,以保证最终一致,并且支持二级缓存。

    在spring环境下,使用@Cached注解可以为一个方法添加缓存,@CacheUpdate 用于更新缓存,@CacheInvalidate 用于移除缓存元素。注解可以加在接口上也可以加在类上,加注解的类必须是一个spring bean,例如官方文档所举例

    public interface UserService {
        @Cached(name="userCache.", key="#userId", expire = 3600)
        User getUserById(long userId);
    
        @CacheUpdate(name="userCache.", key="#user.userId", value="#user")
        void updateUser(User user);
    
        @CacheInvalidate(name="userCache.", key="#userId")
        void deleteUser(long userId);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    5.1 @Cached 的使用

    属性表:

    @Cached 注解和 @CreateCache 的属性非常类似,但是多几个:

    属性默认值说明
    area“default”如果在配置中配置了多个缓存area,在这里指定使用哪个area
    name未定义指定缓存的唯一名称,不是必须的,如果没有指定,会使用类名+方法名。name会被用于远程缓存的key前缀。另外在统计中,一个简短有意义的名字会提高可读性。
    key未定义使用SpEL指定key,如果没有指定会根据所有参数自动生成。
    expire未定义超时时间。如果注解上没有定义,会使用全局配置,如果此时全局配置也没有定义,则为无穷大
    timeUnitTimeUnit.SECONDS指定expire的单位
    cacheTypeCacheType.REMOTE缓存的类型,包括CacheType.REMOTE、CacheType.LOCAL、CacheType.BOTH。如果定义为BOTH,会使用LOCAL和REMOTE组合成两级缓存
    localLimit未定义如果cacheType为LOCAL或BOTH,这个参数指定本地缓存的最大元素数量,以控制内存占用。如果注解上没有定义,会使用全局配置,如果此时全局配置也没有定义,则为100
    localExpire未定义仅当cacheType为BOTH时适用,为内存中的Cache指定一个不一样的超时时间,通常应该小于expire
    serialPolicy未定义指定远程缓存的序列化方式。可选值为SerialPolicy.JAVA和SerialPolicy.KRYO。如果注解上没有定义,会使用全局配置,如果此时全局配置也没有定义,则为SerialPolicy.JAVA
    keyConvertor未定义指定KEY的转换方式,用于将复杂的KEY类型转换为缓存实现可以接受的类型,当前支持KeyConvertor.FASTJSON和KeyConvertor.NONE。NONE表示不转换,FASTJSON可以将复杂对象KEY转换成String。如果注解上没有定义,会使用全局配置。
    enabledtrue是否激活缓存。例如某个dao方法上加缓存注解,由于某些调用场景下不能有缓存,所以可以设置enabled为false,正常调用不会使用缓存,在需要的地方可使用CacheContext.enableCache在回调中激活缓存,缓存激活的标记在ThreadLocal上,该标记被设置后,所有enable=false的缓存都被激活
    cacheNullValuefalse当方法返回值为null的时候是否要缓存
    condition未定义使用SpEL指定条件,如果表达式返回true的时候才去缓存中查询
    postCondition未定义使用SpEL指定条件,如果表达式返回true的时候才更新缓存,该评估在方法执行后进行,因此可以访问到#result

    举例:我在我 TestService 中将查询用户信息的接口 queryUserInfo 添加了一个方法注解,缓存名称为 userCache. ,key 是使用 SpEL 指定方法参数,过期时间设置为 3600秒, 使用本地缓存。

    public interface TestService {
    
        @Cached(name="userCache.", key="#req", expire = 3600,cacheType = CacheType.LOCAL)
        UserInfoResp queryUserInfo(IdReq req);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    可在该方法中打断点,可以看到第一次执行该方法会执行方法中的代码,3600秒内如果用同样的参数去执行该方法是不会经过断点的,那就能够说明是拿了缓存中的数据。

    其次,使用方法缓存还有两个需要注意的就是更新和清楚缓存是怎么操作的,JetCache 和 Spring Cache 差不多,也是通过注解去实现的额,分别是:@CacheUpdate@CacheInvalidate

    5.2 @CacheUpdate 的使用

    作用:更新缓存

    属性表:

    属性默认值说明
    area“default”如果在配置中配置了多个缓存area,在这里指定使用哪个area,指向对应的@Cached定义。
    name未定义指定缓存的唯一名称,指向对应的@Cached定义。
    key未定义使用SpEL指定key
    value未定义使用SpEL指定value
    condition未定义使用SpEL指定条件,如果表达式返回true才执行更新,可访问方法结果#result

    举例:

    public interface TestService {
    
        @Cached(name="userCache.", key="#req", expire = 3600,cacheType = CacheType.LOCAL)
        UserInfoResp queryUserInfo(IdReq req);
    
        @CacheUpdate(name="userCache.", key="#req", value = "#UserInfoResp")
        void saveUserInfo(IdReq req);
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    5.3 @CacheInvalidate 的使用

    作用:删除缓存

    属性表:

    属性默认值说明
    area“default”如果在配置中配置了多个缓存area,在这里指定使用哪个area,指向对应的@Cached定义。
    name未定义指定缓存的唯一名称,指向对应的@Cached定义。
    key未定义使用SpEL指定key
    condition未定义使用SpEL指定条件,如果表达式返回true才执行更新,可访问方法结果#result

    举例:

    public interface TestService {
    
        @Cached(name="userCache.", key="#req", expire = 3600,cacheType = CacheType.LOCAL)
        UserInfoResp queryUserInfo(IdReq req);
    
        @CacheUpdate(name="userCache.", key="#req", value = "#UserInfoResp")
        void saveUserInfo(IdReq req);
    
    	@CacheInvalidate(name="userCache.", key="#req")
        void deleteUserInfo(IdReq req);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    注意:使用@CacheUpdate@CacheInvalidate的时候,相关的缓存操作可能会失败(比如网络IO错误),所以指定缓存的超时时间是非常重要的。

    5.4 @CacheRefresh 的使用

    作用:自动刷新缓存

    属性表:

    属性默认值说明
    refresh未定义刷新间隔
    timeUnitTimeUnit.SECONDS时间单位
    stopRefreshAfterLastAccess未定义指定该key多长时间没有访问就停止刷新,如果不指定会一直刷新
    refreshLockTimeout60秒类型为BOTH/REMOTE的缓存刷新时,同时只会有一台服务器在刷新,这台服务器会在远程缓存放置一个分布式锁,此配置指定该锁的超时时间

    举例:每间隔 10 秒,刷下缓存

    public interface TestService {
    
        @Cached(name="userCache.", key="#req", expire = 3600,cacheType = CacheType.LOCAL)
        @CacheRefresh(refresh = 10)
        UserInfoResp queryUserInfo(IdReq req);
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5.5 @CachePenetrationProtect 的使用

    缓存穿透保护,原理就是当缓存访问未命中的情况下,对并发进行的加载行为进行保护。 当前版本实现的是单JVM内的保护,即同一个JVM中同一个key只有一个线程去加载,其它线程等待结果。

    属性表:

    属性默认值说明
    valuetrue是否开启
    timeout未定义保护时间,默认integer最大值,其他线程等待超时时间,超时后执行方法
    timeUnitTimeUnit.SECONDS时间单位

    举例:

    public interface TestService {
    
        @Cached(name="userCache.", key="#req", expire = 3600,cacheType = CacheType.LOCAL)
        @CachePenetrationProtect
        UserInfoResp queryUserInfo(IdReq req);
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    三、踩坑经验

    1. 启动报错

    我最开始下载 JetCache 依赖坐标使用的是 <version>2.6.0</version> 这个版本的,然后我项目父工程 spring-boot-starter-parent 的版本是 <version>2.1.4.RELEASE</version>,我配置本地缓存方案启动不会报错,当时配置远程缓存方案启动就报错,这是由于jedis 和spring-boot-starter-data-redis 的 maven 依赖的版本不兼容导致, 是经常会出现的问题。所以出现这种情况就去更换 JetCache 的版本,应该就能解决。

    在这里插入图片描述

            <!-- JetCache 缓存 -->
            <dependency>
                <groupId>com.alicp.jetcache</groupId>
                <artifactId>jetcache-starter-redis</artifactId>
                <!-- 更换前 -->
                <!--<version>2.6.0</version>-->
                <!-- 更换后 -->
                <version>2.4.4</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2. @CreateCache 注解失效

    我的代码如下:定义了一个 Cache 缓存,两个方法,一个是往缓存中存数据,一个是往缓存中取数据,分别对应 /put-cache/get-cache 两个接口,不管调哪个接口,都报 NullPointerException,yml 配置和启动注解都有,就是注解没有生效。

    	@CreateCache(area = "default",name = "jetCache_",expire = 3600,timeUnit = TimeUnit.SECONDS,cacheType = CacheType.LOCAL)
        private Cache<String,String> jetCache;
    
        @Override
        public void putCache() {
            String key = "mike";
            jetCache.put(key,"随便存点东西");
        }
    
        @Override
        public void getCache() {
            String key = "mike";
            String value = jetCache.get(key);
            System.out.println("value = " + value);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在这里插入图片描述

    这东西也是依赖的版本不兼容导致的,所以我就继续对照 maven 仓库 尝试寻找一个能够兼容 JetCache 的版本。最后找到了 <version>2.5.9</version> 这一版。PS:个人比较菜,只能通过这种比较笨的方法找了~~

            <!-- JetCache 缓存 -->
            <dependency>
                <groupId>com.alicp.jetcache</groupId>
                <artifactId>jetcache-starter-redis</artifactId>
                <!-- 更换前 -->
                <!--<version>2.6.0</version>-->
                <!-- 更换后 -->
                <!--<version>2.4.4</version>-->
                <!--<version>2.6.5</version>-->
                <!--<version>2.5.3</version>-->
                <version>2.5.9</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    先调 /put-cache 存数据、再调 /get-cache 取数据,控制台输出如下:

    在这里插入图片描述

    可以看到 @CreateCache 注解已经生效了。

    3. @Cached 注解失效

    如果你 @CreateCache 这个注解能够正常使用,但是 @Cached 注解却不行,最大可能的原因就是在启动类上没有加 @EnableMethodCache 注解,或者是 @EnableMethodCache 注解中指定的包路径有错误,请仔细检查。

    在这里插入图片描述


    参考博客:
    阿里开源的缓存框架JetCache
    JetCache简介以及配置说明
    JetCache源码(一)——简介与注解实现
    jetcache的@CreateCache注解不生效
    Alibaba 开源通用缓存访问框架:JetCache
    springboot 环境下 jetcache使用详解

  • 相关阅读:
    JAVA知识管理系统计算机毕业设计Mybatis+系统+数据库+调试部署
    docker-compose快速搭建Zookeeper集群
    叹服,阿里自述 SpringCloud 微服务:入门 + 实战 + 案例,一网打尽
    python+SQL sever+thinter学生宿舍管理系统
    敏捷开发最佳实践:质量维度实践案例之软硬一体持续交付
    Code::Blocks下载和安装教程
    算法精讲!带你轻松搞懂插入排序是咋回事
    大数据成为市场营销利器 ,促进金融贷款企业获客精准化
    Cobalt Strike
    软考信息安全工程师备考
  • 原文地址:https://blog.csdn.net/xhmico/article/details/125543762