本文正在参与「金石计划」
Caffeine作为当下本地缓存的王者被很多的运用再实践的项目中,可以有效的提高服务吞吐率、qps,降低rt
本文将简单介绍下Caffeine的运用姿态
【Spring专栏】内存缓存Caffeine根本运用姿态-技术派
项目装备
1. 依靠
首要搭建一个标准的SpringBoot项目工程,相关版别以及依靠如下
本项目凭借SpringBoot 2.2.1.RELEASE
+ maven 3.5.3
+ IDEA
进行开发
<dependencies>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
</dependencies>
运用实例
引进上面的jar包之后,就可以进入caffeine的运用环节了;咱们首要按照官方wiki来进行演练
- Home zh CN ben-manes/caffeine Wiki
caffeine供给了四种缓存战略,首要是依据手动增加/主动增加,同步/异步来进行区分
其根本运用姿态于Guava差不多
1. 手动加载
private LoadingCache<String, Integer> autoCache;
private AtomicInteger idGen;
public CacheService() {
// 手动缓存加载方法
idGen = new AtomicInteger(100);
uidCache = Caffeine.newBuilder()
// 设置写入后五分钟失效
.expireAfterWrite(5, TimeUnit.MINUTES)
// 设置最多的缓存数量
.maximumSize(100)
.build();
}
1.1 三种失效战略
留意参数设置,咱们先看一下失效战略,共有下面几种
权重:
- maximumSize: 依据容量战略,当缓存内元素个数超过时,经过依据就近度和频率的算法来驱逐掉不会再被运用到的元素
- maximumWeight: 依据权重的容量战略,首要运用于缓存中的元素存在不同的权重场景
时刻:
- expireAfterAccess: 依据访问时刻
- expireAfterWrite: 依据写入时刻
- expireAfter: 可以依据读更新写入来调整有效期
引证:
- weakKeys: 保存的key为弱引证
- weakValues: 保存的value会运用弱引证
- softValues: 保存的value运用软引证
弱引证:这允许在GC的过程中,当没有被任何强引证指向的时分去将缓存元素收回
软引证:在GC过程中被软引证的目标将会被经过LRU算法收回
1.2 缓存增删查姿态
接下来咱们看一下手动方法的运用
public void getUid(String session) {
// 从头再取一次,这次应该就不是从头初始化了
Integer uid = uidCache.getIfPresent(session);
System.out.println("检查缓存! 当没有的时分回来的是 uid: " + uid);
// 第二个参数表明当不存在时,初始化一个,并写入缓存中
uid = uidCache.get(session, (key) -> 10);
System.out.println("初始化一个之后,回来的是: " + uid);
// 移除缓存
uidCache.invalidate(session);
// 手动增加一个缓存
uidCache.put(session + "_2", 11);
// 检查一切的额缓存
Map map = uidCache.asMap();
System.out.println("total: " + map);
// 干掉一切的缓存
uidCache.invalidateAll();
}
查询缓存&增加缓存
-
getIfPresent(key)
: 不存在时,回来null -
get(key, (key) -> {value初始化战略})
: 不存在时,会依据第二个lambda表达式来写入数据,这个就表明的是手动加载缓存 -
asMap
: 获取缓存一切数据
增加缓存
-
put(key, val)
: 主动增加缓存
清空缓存
-
invalidate
: 主动移除缓存 -
invalidateAll
: 失效一切缓存
履行结束之后,输出日志:
检查缓存! 当没有的时分回来的是 uid: null
初始化一个之后,回来的是: 10
total: {02228476-bcd9-412d-b437-bf0092c4a5f6_2=11}
2. 主动加载
在创建的时分,就指定缓存未命中时的加载规矩
// 在创建时,主动指定加载规矩
private LoadingCache<String, Integer> autoCache;
private AtomicInteger idGen;
public CacheService() {
// 手动缓存加载方法
idGen = new AtomicInteger(100);
autoCache = Caffeine.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.maximumSize(100)
.build(new CacheLoader<String, Integer>() {
@Override
public @Nullable Integer load(@NonNull String key) throws Exception {
return idGen.getAndAdd(1);
}
});
}
它的装备,与前面介绍的共同;首要的差异点在于build时,确认缓存值的获取方法
2.1 缓存运用姿态
public void autoGetUid(String session) {
Integer uid = autoCache.getIfPresent(session);
System.out.println("主动加载,没有时回来: " + uid);
uid = autoCache.get(session);
System.out.println("主动加载,没有时主动加载一个: " + uid);
// 批量查询
List<String> keys = Arrays.asList(session, session + "_1");
Map<String, Integer> map = autoCache.getAll(keys);
System.out.println("批量获取,一个存在一个不存在时:" + map);
// 手动加一个
autoCache.put(session + "_2", 11);
Map total = autoCache.asMap();
System.out.println("total: " + total);
}
与前面的差异在于获取缓存值的方法
- get(key): 不用传第二个参数,直接传key获取对应的缓存值,如果没有主动加载数据
- getAll(keys): 可以批量获取数据,若某个key不再缓存中,会主动加载;在里面的则直接运用缓存的
实践输出结果如下
主动加载,没有时回来: null
主动加载,没有时主动加载一个: 100
批量获取,一个存在一个不存在时:{02228476-bcd9-412d-b437-bf0092c4a5f6=100, 02228476-bcd9-412d-b437-bf0092c4a5f6_1=101}
total: {02228476-bcd9-412d-b437-bf0092c4a5f6_2=11, 02228476-bcd9-412d-b437-bf0092c4a5f6_1=101, 02228476-bcd9-412d-b437-bf0092c4a5f6=100}
3.异步手动加载
异步,首要是值在获取换粗内容时,选用的异步战略;运用与前面没有什么太大差别
// 手动异步加载缓存
private AsyncCache<String, Integer> asyncUidCache;
public CacheService() {
asyncUidCache = Caffeine.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.maximumSize(100)
.buildAsync();
}
3.1 缓存运用姿态
public void asyncGetUid(String session) throws ExecutionException, InterruptedException {
// 从头再取一次,这次应该就不是从头初始化了
CompletableFuture<Integer> uid = asyncUidCache.getIfPresent(session);
System.out.println("检查缓存! 当没有的时分回来的是 uid: " + (uid == null ? "null" : uid.get()));
// 第二个参数表明当不存在时,初始化一个,并写入缓存中
uid = asyncUidCache.get(session, (key) -> 10);
System.out.println("初始化一个之后,回来的是: " + uid.get());
// 手动塞入一个缓存
asyncUidCache.put(session + "_2", CompletableFuture.supplyAsync(() -> 12));
// 移除缓存
asyncUidCache.synchronous().invalidate(session);
// 检查一切的额缓存
System.out.println("print total cache:");
for (Map.Entry<String, CompletableFuture<Integer>> sub : asyncUidCache.asMap().entrySet()) {
System.out.println(sub.getKey() + "==>" + sub.getValue().get());
}
System.out.println("total over");
}
- getIfPresent: 存在时回来CompletableFuture,不存在时回来null,因此留意npe的问题
- get(key, Function<>): 第二个参数表明加载数据的逻辑
- put(key, CompletableFuture<>): 手动参加缓存,留意这里也不是直接加一个详细的value到缓存
- synchronous().invalidate() : 同步清除缓存
- getAll: 一次获取多个缓存,同样的是在缓存的取缓存,不在的依据第二个传参进行加载
与前面相比,运用姿态差不多,仅有留意的是,获取的并不是直接的结果,而是CompletableFuture,上面履行之后的输出如下:
检查缓存! 当没有的时分回来的是 uid: null
初始化一个之后,回来的是: 10
print total cache:
5dd53310-aec7-42a5-957e-f7492719c29d_2==>12
total over
4. 异步主动加载
在定义缓存时,就指定了缓存不存在的加载逻辑;与第二个相比差异在于这里是异步加载数据到缓存中
private AtomicInteger idGen;
// 主动异步加载缓存
private AsyncLoadingCache<String, Integer> asyncAutoCache;
public CacheService() {
idGen = new AtomicInteger(100);
asyncAutoCache = Caffeine.newBuilder()
.expireAfterWrite(5, TimeUnit.MINUTES)
.maximumSize(100)
.buildAsync(new CacheLoader<String, Integer>() {
@Override
public @Nullable Integer load(@NonNull String key) throws Exception {
return idGen.getAndAdd(1);
}
});
}
4.1 缓存运用姿态
public void asyncAutoGetUid(String session) {
try {
CompletableFuture<Integer> uid = asyncAutoCache.getIfPresent(session);
System.out.println("主动加载,没有时回来: " + (uid == null ? "null" : uid.get()));
uid = asyncAutoCache.get(session);
System.out.println("主动加载,没有时主动加载一个: " + uid.get());
// 批量查询
List<String> keys = Arrays.asList(session, session + "_1");
CompletableFuture<Map<String, Integer>> map = asyncAutoCache.getAll(keys);
System.out.println("批量获取,一个存在一个不存在时:" + map.get());
// 手动加一个
asyncAutoCache.put(session + "_2", CompletableFuture.supplyAsync(() -> 11));
// 检查一切的额缓存
System.out.println("print total cache:");
for (Map.Entry<String, CompletableFuture<Integer>> sub : asyncAutoCache.asMap().entrySet()) {
System.out.println(sub.getKey() + "==>" + sub.getValue().get());
}
System.out.println("total over");
// 清空一切缓存
asyncAutoCache.synchronous().invalidateAll();
} catch (Exception e) {
e.printStackTrace();
}
}
输出:
主动加载,没有时回来: null
主动加载,没有时主动加载一个: 102
批量获取,一个存在一个不存在时:{5dd53310-aec7-42a5-957e-f7492719c29d=102, 5dd53310-aec7-42a5-957e-f7492719c29d_1=103}
print total cache:
5dd53310-aec7-42a5-957e-f7492719c29d_2==>11
5dd53310-aec7-42a5-957e-f7492719c29d_1==>103
5dd53310-aec7-42a5-957e-f7492719c29d==>102
total over
不能错失的源码和相关知识点
0. 项目
- 工程:github.com/liuyueyi/sp…
- 源码:github.com/liuyueyi/sp…
1. 微信大众号: 一灰灰Blog
尽信书则不如,以上内容,纯属一家之言,因个人能力有限,不免有遗漏和过错之处,如发现bug或许有更好的主张,欢迎批评指正,不惜感激
下面一灰灰的个人博客,记载一切学习和工作中的博文,欢迎大家前去逛逛
- 一灰灰Blog个人博客 blog.hhui.top
- 一灰灰Blog-Spring专题博客 spring.hhui.top