JetCache是一个基于Java的缓存系统封装, 提供统一的API和注解来简化缓存的使用, 可以支持:
- TTL
- 两级缓存
- 分布式自动刷新
- 通过注解创建并配置缓存
- 缓存使用的统计
- Key生成策略与Value序列化方式自定义
- 异步CacheAPI
- Spring Boot支持
要求
- JDK1.8
- 可选Spring Framework 4.0.8+
- 可选Spring Boot 1.1.9+
基本使用
仅仅介绍 SpringBoot 下的使用
快速开始
1 | <dependency> |
1 | jetcache: |
1 |
|
创建缓存实例的方式
一级缓存
1 |
|
二级缓存
1 |
|
创建方法缓存的方式
1 | public interface UserService { |
CacheAPI
缓存的写入与读取都通过这个进行
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
基本api如下:
```java
// 获取
V get(K key)
// 插入
void put(K key, V value);
// 指定过期时间
void put(K key, V value, long expire, TimeUnit timeUnit)
boolean putIfAbsent(K key, V value); //多级缓存MultiLevelCache不支持此方法
boolean remove(K key);
<T> T unwrap(Class<T> clazz);//2.2版本前,多级缓存MultiLevelCache不支持此方法
Map<K,V> getAll(Set<? extends K> keys);
void putAll(Map<? extends K,? extends V> map);
void removeAll(Set<? extends K> keys);
// 当key对应的缓存不存在时,使用loader加载。
// cacheNullWhenLoaderReturnNull参数指定了当loader加载出来时null值的时候,是否要进行缓存(有时候即使是null值也是通过很繁重的查询才得到的,需要缓存)。
// expire和timeUnit指定了缓存的超时时间,会覆盖缓存的默认超时时间。
V computeIfAbsent(K key, Function<K, V> loader)
V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull)
V computeIfAbsent(K key, Function<K, V> loader, boolean cacheNullWhenLoaderReturnNull, long expire, TimeUnit timeUnit)
// 尝试获取锁
AutoReleaseLock tryLock(K key, long expire, TimeUnit timeUnit) //可能获取到AutoReleaseLock为null,需要自行判断
boolean tryLockAndRun(K key, long expire, TimeUnit timeUnit, Runnable action)
特殊的API
jetCache提供了大写的API, 与小写的区别是返回一个CacheResult对象, 例如:可以通过大写的
GET
判断获取到的key不存在还是发生了异常情况导致没有获取到,还是缓存过期导致没有获取到.
1 | CacheGetResult<V> GET(K key); |
使用上要这样
1 | CacheGetResult<OrderDO> r = cache.GET(orderId); |
注解的使用
成员变量上的注解CreateCache
有如下参数:
属性 | 默认值 | 说明 |
---|---|---|
area | “default” | 如果需要连接多个缓存系统,可在配置多个cache area,这个属性指定要使用的那个area的name |
name | 未定义 | 指定缓存的名称,不是必须的,如果没有指定,会使用类名+方法名。name会被用于远程缓存的key前缀。另外在统计中,一个简短有意义的名字会提高可读性。如果两个@CreateCache 的name 和area 相同,它们会指向同一个Cache 实例 |
expire | 未定义 | 该Cache实例的默认超时时间定义,注解上没有定义的时候会使用全局配置,如果此时全局配置也没有定义,则取无穷大 |
timeUnit | TimeUnit.SECONDS | 指定expire的单位 |
cacheType | CacheType.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。如果注解上没有定义,则使用全局配置。 |
方法上的注解Cached,CacheUpdate,CacheInvalidate,CacheRefresh,CachePenetrationProtect
Cached
属性 | 默认值 | 说明 |
---|---|---|
area | “default” | 如果在配置中配置了多个缓存area,在这里指定使用哪个area |
name | 未定义 | 指定缓存的唯一名称,不是必须的,如果没有指定,会使用类名+方法名。name会被用于远程缓存的key前缀。另外在统计中,一个简短有意义的名字会提高可读性。 |
key | 未定义 | 使用SpEL指定key,如果没有指定会根据所有参数自动生成。 |
expire | 未定义 | 超时时间。如果注解上没有定义,会使用全局配置,如果此时全局配置也没有定义,则为无穷大 |
timeUnit | TimeUnit.SECONDS | 指定expire的单位 |
cacheType | CacheType.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。如果注解上没有定义,会使用全局配置。 |
enabled | true | 是否激活缓存。例如某个dao方法上加缓存注解,由于某些调用场景下不能有缓存,所以可以设置enabled为false,正常调用不会使用缓存,在需要的地方可使用CacheContext.enableCache在回调中激活缓存,缓存激活的标记在ThreadLocal上,该标记被设置后,所有enable=false的缓存都被激活 |
cacheNullValue | false | 当方法返回值为null的时候是否要缓存 |
condition | 未定义 | 使用SpEL指定条件,如果表达式返回true的时候才去缓存中查询 |
postCondition | 未定义 | 使用SpEL指定条件,如果表达式返回true的时候才更新缓存,该评估在方法执行后进行,因此可以访问到#result |
CacheUpdate
属性 | 默认值 | 说明 |
---|---|---|
area | “default” | 如果在配置中配置了多个缓存area,在这里指定使用哪个area,指向对应的@Cached定义。 |
name | 未定义 | 指定缓存的唯一名称,指向对应的@Cached定义。 |
key | 未定义 | 使用SpEL指定key |
value | 未定义 | 使用SpEL指定value |
condition | 未定义 | 使用SpEL指定条件,如果表达式返回true才执行更新,可访问方法结果#result |
CacheInvalidate
属性 | 默认值 | 说明 |
---|---|---|
area | “default” | 如果在配置中配置了多个缓存area,在这里指定使用哪个area,指向对应的@Cached定义。 |
name | 未定义 | 指定缓存的唯一名称,指向对应的@Cached定义。 |
key | 未定义 | 使用SpEL指定key |
condition | 未定义 | 使用SpEL指定条件,如果表达式返回true才执行删除,可访问方法结果#result |
CacheRefresh
属性 | 默认值 | 说明 |
---|---|---|
refresh | 未定义 | 刷新间隔 |
timeUnit | TimeUnit.SECONDS | 时间单位 |
stopRefreshAfterLastAccess | 未定义 | 指定该key多长时间没有访问就停止刷新,如果不指定会一直刷新 |
refreshLockTimeout | 60秒 | 类型为BOTH/REMOTE的缓存刷新时,同时只会有一台服务器在刷新,这台服务器会在远程缓存放置一个分布式锁,此配置指定该锁的超时时间 |
CachePenetrationProtect
放穿透机制
当缓存访问未命中的情况下,对并发进行的加载行为进行保护。
当前版本实现的是单JVM内的保护,即同一个JVM中同一个key只有一个线程去加载,其它线程等待结果。
配置
1 | jetcache: |
配置通用说明如下
属性 | 默认值 | 说明 |
---|---|---|
jetcache.statIntervalMinutes |
0 | 统计间隔,0表示不统计 |
jetcache.areaInCacheName |
true | jetcache-anno把cacheName作为远程缓存key前缀,2.4.3以前的版本总是把areaName加在cacheName中,因此areaName也出现在key前缀中。2.4.4以后可以配置,为了保持远程key兼容默认值为true,但是新项目的话false更合理些。 |
jetcache.hiddenPackages |
无 | @Cached和@CreateCache自动生成name的时候,为了不让name太长,hiddenPackages指定的包名前缀被截掉 |
jetcache.[local\remote].${area}.type |
无 | 缓存类型。tair、redis为当前支持的远程缓存;linkedhashmap、caffeine为当前支持的本地缓存类型 |
jetcache.[local\remote].${area}.keyConvertor |
无 | key转换器的全局配置,当前只有一个已经实现的keyConvertor:fastjson 。仅当使用@CreateCache且缓存类型为LOCAL时可以指定为none ,此时通过equals方法来识别key。方法缓存必须指定keyConvertor |
jetcache.[local\remote].${area}.valueEncoder |
java | 序列化器的全局配置。仅remote类型的缓存需要指定,可选java和kryo |
jetcache.[local\remote].${area}.valueDecoder |
java | 序列化器的全局配置。仅remote类型的缓存需要指定,可选java和kryo |
jetcache.[local\remote].${area}.limit |
100 | 每个缓存实例的最大元素的全局配置,仅local类型的缓存需要指定。注意是每个缓存实例的限制,而不是全部,比如这里指定100,然后用@CreateCache创建了两个缓存实例(并且注解上没有设置localLimit属性),那么每个缓存实例的限制都是100 |
jetcache.[local\remote].${area}.expireAfterWriteInMillis |
无穷大 | 以毫秒为单位指定超时时间的全局配置(以前为defaultExpireInMillis) |
jetcache.local.${area}.expireAfterAccessInMillis |
0 | 需要jetcache2.2以上,以毫秒为单位,指定多长时间没有访问,就让缓存失效,当前只有本地缓存支持。0表示不使用这个功能。 |
上表中${area}对应@Cached和@CreateCache的area属性。注意如果注解上没有指定area,默认值是”default”。
多个地方能配置过期时间,优先级为: 方法传参>注解参数>全局参数
高级功能
异步API
大写API会返回CacheGetResult对象,CacheGetResult对象的future()
方法可以返回一个CompletionStage,能支持异步调用某些逻辑.
1 | CacheGetResult<UserDO> r = cache.GET(userId); |
无返回值的操作使用异步调用,能减少RT
自动load
Cache的get方法有时候会出现没有命中的情况, 可以设置一个loader, 没有命中时自动的加载.
1 | // loadUserFromDatabase |
如果loader抛出异常,get和getAll会抛出CacheInvokeException。
大写GET,GET_ALL不调用loader; 使用多级缓存时, loader不要设置到子Cache中, 应该是父Cache(MultiLevelCache)上
@CreateCache情况下使用, 例子如下
1 | @CreateCache |
自动刷新缓存
能设置刷新策略, 防止缓存过期.
@CreateCache情况下使用, 例子如下
1 |
|
远程缓存
Jedis
jetcache-starter-redis
配置:
1 | jetcache: |
如果需要直接操作JedisPool,可以通过以下方式获取
1 |
|
然后可以直接使用
1 |
|
Lettuce
jetcache-starter-redis-lettuce
配置
1 | jetcache: |
如果需要直接使用lettuce的RedisClient:
1 |
|
然后可以直接使用
1 |
|
本地缓存
LinkedHashMapCache
LinkedHashMapCache是JetCache中实现的一个最简单的Cache,使用LinkedHashMap做LRU方式淘汰。
1 | Cache<Long, OrderDO> cache = LinkedHashMapCacheBuilder.createLinkedHashMapCacheBuilder() |
CaffeineCache
caffeine cache的介绍看这里,它是guava cache的后续作品。
1 | Cache<Long, OrderDO> cache = CaffeineCacheBuilder.createCaffeineCacheBuilder() |
缓存的统计与监控
jetcache.statIntervalMinutes大于0时, jetCache会自动在日志上打印缓存的使用情况.
1 | 2017-01-12 19:00:00,001 INFO support.StatInfoLogger - jetcache stat from 2017-01-12 18:59:00,000 to 2017-01-12 19:00:00,000 |
只有使用computeIfAbsent方法或者@Cached注解才会统计loadTime。用get方法取缓存,没有命中的话自己去数据库load,显然是无法统计到的。
也可以自定义输出
1 | // for 2.6+ |
1 | // for 2.5 |
可以输出成单独的log文件
1 | <appender name="JETCACHE_LOGFILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> |
不使用注解
不使用注解来创建缓存实例要利用各种Builder
1 | // 本地 |
最后
注意
- key、condition等表达式中使用参数名需要javac命令的
-parameters
支持1
2
3
4
5
6
7
8
9
10
11
12<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgument>-parameters</compilerArgument>
</configuration>
</plugin>
</plugins> - 自定义序列化器可以自定义SpringConfigProvider的Bean,实现
parseValueEncoder
和parseValueDecoder
方法
看源码
Cache的实现:
- RedisCache: redis实现,使用jedis客户端
- RedisLettuceCache: redis实现,使用lettuce客户端
- CaffeineCache: 基于内存的缓存,使用Caffeine
- LinkedHashMapCache: 自制的简易内存缓存,没有任何依赖
- LoadingCache:基于Decorator模式,提供自动加载功能
- RefreshCache:基于Decorator模式,提供自动刷新功能
- MultiLevelCache:多级缓存,注解方式配置只支持了两级,实际上这个类支持N级
jetcache-anno提供了注解支持,顺着这两个注解类看就可以了:
- EnableCreateCacheAnnotation
- EnableMethodCache