Skip to content

Cache 使用说明

comet-cache 模块集成了 JetCache 两级缓存中间件,支持内存 + redis 的两级缓存,具体使用如下说明。

Jetcache 简介

JetCache 是一个基于 Java 的缓存系统封装,提供统一的 API 和注解来简化缓存的使用。

JetCache 提供了比 SpringCache 更加强大的注解,可以原生的支持 TTL、两级缓存、分布式自动刷新,还提供了 Cache 接口用于手工缓存操作。

当前有四个实现,RedisCacheTairCache(此部分未在github开源)、CaffeineCache(in memory)、和一个简易的 LinkedHashMapCache(in memory),也只支持添加新的实现。

要求

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

快速开始

基于 springboot 的方式基本配置

POM

xml
<dependency>
    <groupId>com.dcits</groupId>
    <artifactId>comet-cache</artifactId>
    <version>${comet.version}</version>
</dependency>

${comet.version} 修改为具体的版本号。

springboot 配置方式

配置一个 spring boot 风格的 application.yml 文件,把他放到资源目录中,配置文件详细信息请看配置说明章节。

yaml
jetcache:
  statIntervalMinutes: 15
  areaInCacheName: false
  local:
    default:
      type: linkedhashmap
      keyConvertor: fastjson
  remote:
    default:
      type: redis
      keyConvertor: fastjson
      valueEncoder: java
      valueDecoder: java
      poolConfig:
        minIdle: 5
        maxIdle: 20
        maxTotal: 50
      host: 127.0.0.1
      port: 6379

在项目启动类下,EnableMethodCacheEnableCreateCacheAnnotation 这两个注解分别激活 Cached 和 CreateCache 注解。

java
package com.company.mypackage;

import com.alicp.jetcache.anno.config.EnableCreateCacheAnnotation;
import com.alicp.jetcache.anno.config.EnableMethodCache;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableMethodCache(basePackages = "com.company.mypackage")
@EnableCreateCacheAnnotation
public class MySpringBootApp {
    public static void main(String[] args) {
        SpringApplication.run(MySpringBootApp.class);
    }
}

使用 @CreateCache@Cached 注解添加缓存。

创建缓存实例

通过 @CreateCache 注解创建一个缓存实例,默认超时时间是 100 秒。

@CreateCache(expire = 100)
private Cache<Long, UserDO> userCache;

用起来就像 map 一样。

UserDO user = userCache.get(123L);
userCache.put(123L, user);
userCache.remove(123L);

创建一个两级(内存 + 远程)的缓存,内存中的元素个数限制在 50 个。

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

name 属性不是必须的,name 用于展示统计数据的使用。如果同一个 area 两个 @CreateCachename 配置一样,它们生成的 Cache 将指向同一个实例。

创建方法缓存

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

public interface UserService {
    //添加缓存
    @Cached(name="UserService.getUserById", 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);
}

配置说明

yml 配置文件方式,如果没使用 springboot,直接配置 GlobalCacheConfig 是类似的:

jetcache:
  statIntervalMinutes: 15
  areaInCacheName: false
  hidePackages: com.alibaba
  local:
    default:
      type: caffeine
      limit: 100
      keyConvertor: fastjson
      expireAfterWriteInMillis: 100000
    otherArea:
      type: linkedhashmap
      limit: 100
      keyConvertor: none
      expireAfterWriteInMillis: 100000
  remote:
    default:
      type: redis
      keyConvertor: fastjson
      valueEncoder: java
      valueDecoder: java
      poolConfig:
        minIdle: 5
        maxIdle: 20
        maxTotal: 50
      host: ${redis.host}
      port: ${redis.port}
    otherArea:
      type: redis
      keyConvertor: fastjson
      valueEncoder: kryo
      valueDecoder: kryo
      poolConfig:
        minIdle: 5
        maxIdle: 20
        maxTotal: 50
      host: ${redis.host}
      port: ${redis.port}

关于缓存的超时时间,有多个地方指定,生效规则如下:

  1. put 等方法上指定了超时时间,则以此时间为准
java
userCache.put("name", "dcits", 1000, TimeUnit.SECONDS);
  1. put 等方法上未指定超时时间,使用 Cache 实例的默认超时时间

  2. Cache 实例的默认超时时间,通过在 @CreateCache@Cached 上的 expire 属性指定,如果没有指定,使用 yml 中定义的全局配置;

例如 @Cached(cacheType=local) 使用 jetcache.local.default.expireAfterWriteInMillis,如果仍未指定则是无穷大。

如果需要 redis 集群、读写分离、异步等特性,请参考以下配置:

从 JetCache2.2 版本开始,增加了对 lettuce 客户端的支持,JetCache 的 lettuce 支持提供了异步操作和 redis 集群支持。

application.yml 文件如下(这里省去了 local 相关的配置):

yaml
jetcache: 
  areaInCacheName: false
  remote:
    default:
      type: redis.lettuce
      keyConvertor: fastjson
      uri: redis://127.0.0.1:6379/
      #uri: redis-sentinel://127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381/?sentinelMasterId=mymaster
      #readFrom: slavePreferred

如果使用 sentinel 做自动主备切换,uri 可以配置为 redis-sentinel://127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381/?sentinelMasterId=mymaster

如果是集群:

yaml
jetcache: 
  areaInCacheName: false
  remote:
    default:
      type: redis.lettuce
      keyConvertor: fastjson
      #readFrom: slavePreferred
      uri:
        - redis://127.0.0.1:7000
        - redis://127.0.0.1:7001
        - redis://127.0.0.1:7002

@CreateCache 注解说明

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

java
@CreateCache(expire = 100)
private Cache<Long, UserDO> userCache;

@CreateCache 属性说明

属性默认值说明
area“default”如果需要连接多个缓存系统,可在配置多个cache area,这个属性指定要使用的那个area的name
name未定义指定缓存的名称,不是必须的,如果没有指定,会使用类名+方法名。name会被用于远程缓存的key前缀。另外在统计中,一个简短有意义的名字会提高可读性。如果两个@CreateCachenamearea相同,它们会指向同一个Cache实例
expire未定义该Cache实例的默认超时时间定义,注解上没有定义的时候会使用全局配置,如果此时全局配置也没有定义,则取无穷大
timeUnitTimeUnit.SECONDS指定expire的单位
cacheTypeCacheType.REMOTE缓存的类型,包括CacheType.REMOTE、CacheType.LOCAL、CacheType.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。如果注解上没有定义,则使用全局配置。

对于以上未定义默认值的参数,如果没有指定,将使用 yml 中指定的全局配置

方法级缓存说明

JetCache 方法缓存和 SpringCache 比较类似,它原生提供了 TTL 支持,以保证最终一致,并且支持二级缓存。JetCache2.4 以后支持基于注解的缓存更新和删除。

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

java
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);
}

key 使用 Spring 的 SpEL 脚本来指定。如果要使用参数名(比如这里的 key="#userId" ),项目编译设置 target 必须为 1.8 格式,并且指定 javac 的 -parameters 参数,否则就要使用 key="args[0]" 这样按下标访问的形式。

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

@Cached 注解属性说明

属性默认值说明
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

@CacheInvalidate 注解说明

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

@CacheUpdate 注解说明

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

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

@CacheRefresh 注解说明

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

@CachePenetrationProtect 注解:

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

对于以上未定义默认值的参数,如果没有指定,将使用 yml 中指定的全局配置。

Bulider 创建缓存

JetCache2 版本的 @Cached@CreateCache 等注解都是基于 Spring4.X 版本实现的,在没有 Spring 支持的情况下,注解将不能使用。但是可以直接使用 JetCache 的 API 来创建、管理、监控 Cache,多级缓存也可以使用。

创建缓存

创建缓存的操作类似 guava/caffeine cache,例如下面的代码创建基于内存的 LinkedHashMapCache:

java
Cache<String, Integer> cache = LinkedHashMapCacheBuilder.createLinkedHashMapCacheBuilder()
        .limit(100)
        .expireAfterWrite(200, TimeUnit.SECONDS)
        .buildCache();

创建 RedisCache:

java
GenericObjectPoolConfig pc = new GenericObjectPoolConfig();

pc.setMinIdle(2);
pc.setMaxIdle(10);
pc.setMaxTotal(10);

JedisPool pool = new JedisPool(pc, "localhost", 6379);

Cache<Long, OrderDO> orderCache = RedisCacheBuilder.createRedisCacheBuilder()
        .keyConvertor(FastjsonKeyConvertor.INSTANCE)
        .valueEncoder(JavaValueEncoder.INSTANCE)
        .valueDecoder(JavaValueDecoder.INSTANCE)
        .jedisPool(pool)
        .keyPrefix("orderCache")
        .expireAfterWrite(200, TimeUnit.SECONDS)
        .buildCache();

多级缓存

在 2.2 以后通过下面的方式创建多级缓存:

java
Cache multiLevelCache = MultiLevelCacheBuilder.createMultiLevelCacheBuilder()
        .addCache(memoryCache, redisCache)
        .expireAfterWrite(100, TimeUnit.SECONDS)
        .buildCache();

实际上,使用 MultiLevelCache 可以创建多级缓存,它的构造函数接收的是一个 Cache 数组(可变参数)。

如果是 2.2 之前的版本:

java
Cache memoryCache = ...
Cache redisCache = ...
Cache multiLevelCache = new MultiLevelCache(memoryCache, redisCache);

监控统计

如果要对 Cache 进行监控统计:

java
Cache orderCache = ...
CacheMonitor orderCacheMonitor = new DefaultCacheMonitor("OrderCache");
orderCache.config().getMonitors().add(orderCacheMonitor); // jetcache 2.2+, or call builder.addMonitor() before buildCache()

// Cache<Long, Order> monitedOrderCache = new MonitoredCache(orderCache, orderCacheMonitor); //before jetcache 2.2
int resetTime = 1;
boolean verboseLog = false;
DefaultCacheMonitorManager cacheMonitorManager = new DefaultCacheMonitorManager(resetTime, TimeUnit.SECONDS, verboseLog);
cacheMonitorManager.add(orderCacheMonitor);
cacheMonitorManager.start();

首先创建一个 CacheMonitor,每个 DefaultCacheMonitor 只能用于一个 Cache。当 DefaultCacheMonitorManager 启动以后,会使用 slf4j 按指定的时间定期输出统计信息到日志中,DefaultCacheMonitor 构造时指定的名字会作为输出时 cache 的名字。

在组装多级缓存的过程中,可以给每个缓存安装一个 Monitor,这样可以监控每一级的命中情况。

也可以自己对统计信息进行处理,调用下面的构造方法创建 DefaultCacheMonitorManager

java
public DefaultCacheMonitorManager(int resetTime, TimeUnit resetTimeUnit, Consumer<StatInfo> statCallback)

参考链接

关于 jetCache 更多的使用请参考:https://github.com/alibaba/jetcache/wiki