阿里开源 JetCache 缓存框架介绍使用

Posted 小毕超

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了阿里开源 JetCache 缓存框架介绍使用相关的知识,希望对你有一定的参考价值。

一、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支持

github地址: https://github.com/alibaba/jetcache

注意:

JetCache需要JDK1.8、Spring Framework4.0.8以上版本。Spring Boot为可选,需要1.1.9以上版本。如果不使用注解(仅使用jetcache-core),Spring Framework也是可选的,此时使用方式与Guava/Caffeine cache类似。

二、SpringBoot 引入

  1. pom
 <!-- https://mvnrepository.com/artifact/com.alicp.jetcache/jetcache-starter-redis-lettuce -->
 <dependency>
     <groupId>com.alicp.jetcache</groupId>
     <artifactId>jetcache-starter-redis-lettuce</artifactId>
     <version>2.6.0.M1</version>
 </dependency>

 <dependency>
     <groupId>com.github.ben-manes.caffeine</groupId>
     <artifactId>caffeine</artifactId>
 </dependency>

 <!-- 添加对redis的支持 -->
 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
 </dependency>

 <!-- 添加对连接池的支持 -->
 <dependency>
     <groupId>org.apache.commons</groupId>
     <artifactId>commons-pool2</artifactId>
 </dependency>
  1. application.yml
jetcache:
  statIntervalMinutes: 1 #统计间隔
  areaInCacheName: false
  local:
    default: #默认area
      type: caffeine
      keyConvertor: fastjson
  remote:
    default:
      type: redis.lettuce #使用lettuce
      keyConvertor: fastjson
      valueEncoder: java
      valueDecoder: java
      poolConfig:
        minIdle: 1
        maxIdle: 50
        maxTotal: 1000
        maxWait: 1000
#      uri: ['redis://password@192.168.0.1:6379/0','redis://password@192.168.0.2:6379/0','redis://password@192.168.0.3:6379/0']
      uri:
        - redis://password@192.168.0.1:6379/0  #redis://密码@IP:端口/库
        - redis://password@192.168.0.2:6379/0
        - redis://password@192.168.0.3:6379/0
      readFrom: masterPreferred #master优先
  1. 启动类开启缓存
@SpringBootApplication
@EnableCreateCacheAnnotation
@EnableMethodCache(basePackages = { "com.bxc.jetcache.jetcachedemo.service" })
public class JetcacheDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(JetcacheDemoApplication.class, args);
    }

}

三、方法缓存

1. @Cached

使用@Cached方法可以为一个方法添加上缓存。JetCache通过Spring AOP生成代理,来支持缓存功能。注解可以加在接口方法上也可以加在类方法上,但需要保证是个Spring bean。

@Cached(name = "bxcCache:", cacheType = CacheType.BOTH, key = "#id", expire = 30, timeUnit = TimeUnit.MINUTES)
public String getCache(String id) {
    System.out.println("哈哈哈哈");
    return "abc";
}

@CacheUpdate(name = "bxcCache:", key = "#id", value = "#result")
public String updateCache(String id) {
    return "update";
}

@CacheInvalidate(name = "bxcCache:", key = "#id")
public String deleteCache(String id){
    return "delete";
}

上面代码中可以看出,我们可以使用SpEL(Spring Expression Language)来设置key和Value。name属性不是必须的,但是起个名字是个好习惯,展示统计数据的使用,会使用这个名字。如果同一个area两个@CreateCache的name配置一样,它们生成的Cache将指向同一个实例。这里面需要注意的是,java代码的编辑级别必须是1.8。

其中CacheType

CacheType.REMOTE 远程缓存,如:redis
CacheType.LOCAL本地缓存,如:linkedhashmap、caffeine
CacheType.BOTH 本地+远程缓存,二级缓存

@CacheUpdate和@CacheInvalidate的name和area属性必须和@Cached相同,name属性还会用做cache的key前缀。

@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

2. @CacheRefresh 自动刷新

@CacheRefresh 相当于开始一个定时任务,定时刷新值到缓存中。

@Cached(name = "bxcCache:", cacheType = CacheType.BOTH, key = "#id", expire = 30, timeUnit = TimeUnit.MINUTES)
@CacheRefresh(refresh = 2, stopRefreshAfterLastAccess = 10, timeUnit = TimeUnit.SECONDS)
public String getCache(String id) {
    System.out.println("哈哈哈哈");
    return "abc";
}

refresh 刷新时间周期
stopRefreshAfterLastAccess 停止刷新周期
timeUnit 时间单位

运行效果:

@CacheRefresh注解说明:

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

3. @CachePenetrationProtect 同步加载数据

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

@Cached(name = "bxcCache:", cacheType = CacheType.BOTH, key = "#id", expire = 30, timeUnit = TimeUnit.MINUTES)
@CachePenetrationProtect
public String getCache(String id) {
   System.out.println("哈哈哈哈");
   return "abc";
}

参数解析:

四、缓存API

使用@CreateCache注解去创建一个Cache实例

@CreateCache(expire = 100, cacheType = CacheType.BOTH, localLimit = 50)
private Cache<Long, UserDO> userCache;

expire 超时时间
cacheType 缓存类型
localLimit 限制缓存数量

//写入缓存
cache.put("abc", "aaa");
//读取缓存
System.out.println("1 > " + cache.get("abc"));

System.out.println("2 > "+ cache.computeIfAbsent("abc1", res -> {
   return "111";
}));

System.out.println("3 > " + cache.computeIfAbsent("abc2", res -> {
   return "222";
}, false, 10, TimeUnit.SECONDS));

五、异步API

从JetCache2.2版本开始,所有的大写API返回的CacheResult都支持异步。当底层的缓存实现支持异步的时候,大写API返回的结果都是异步的。当前支持异步的实现只有jetcache的redis-luttece实现,其他的缓存实现(内存中的、Tair、Jedis等),所有的异步接口都会同步堵塞,这样API仍然是兼容的。

CacheGetResult<UserDO> r = cache.GET(userId);

这一行代码执行完以后,缓存操作可能还没有完成,如果此时调用r.isSuccess()或者r.getValue()或者r.getMessage()将会堵塞直到缓存操作完成。如果不想被堵塞,并且需要在缓存操作完成以后执行后续操作,可以这样做:

CompletionStage<ResultData> future = r.future();
future.thenRun(() -> {
    if(r.isSuccess()){
        System.out.println(r.getValue());
    }
});

以上代码将会在缓存操作异步完成后,在完成异步操作的线程中调用thenRun中指定的回调。CompletionStage是Java8新增的功能,如果对此不太熟悉可以先查阅相关的文档。需要注意的是,既然已经选择了异步的开发方式,在回调中不能调用堵塞方法,以免堵塞其他的线程(回调方法很可能是在event loop线程中执行的)。

部分小写的api不需要任何修改,就可以直接享受到异步开发的好处。比如put和removeAll方法,由于它们没有返回值,所以此时就直接优化成异步调用,能够减少RT;而get方法由于需要取返回值,所以仍然会堵塞。

六、分布式锁

boolean hasRun = cache.tryLockAndRun("liuxw3", 100, TimeUnit.SECONDS, () -> {
     System.out.println("我获取到锁了");
});

以上是关于阿里开源 JetCache 缓存框架介绍使用的主要内容,如果未能解决你的问题,请参考以下文章

jetcache缓存官网教程

203.阿里jetcache

Laravel框架怎样使用阿里云ACE缓存服务

阿里开源Sentinel流控框架基本介绍与简单使用

刚刚,阿里开源首个深度学习框架 X-Deep Learning!

Android 开源框架Universal-Image-Loader完全解析--- 图片缓存策略详解