• 主页
  • 读书笔记
  • 备案号:滇ICP备19009238号
所有文章 其他站点 关于我

  • 主页
  • 读书笔记
  • 备案号:滇ICP备19009238号

Caffeine简单demo

2022-07-04

简介

Caffeine 是一款基于 jdk8 实现的缓存工具,在设计上参考了 google 的 Guava cach 组件,可以理解为是一个 GuavaCache 的加强版本。

适用场景

因为 Caffeine cache 是类似于 Guava cache 的一种内存缓存,所以适合单机的数据缓存;因为存储在内存的,没有持久化,因此适合一些短期或者启动以及结果信息的短暂缓存。当涉及到多机多服务的缓存时候,属于分布式缓存的范畴,可以使用 Redis、memcached 等分布式的缓存组件。

缓存填充策略

手动

手动加载:手动控制缓存的增删改处理,主动增加、获取以及依据函数式更新缓存;底层使用 ConcurrentHashMap 进行节点存储,因此 get () 方法是安全的。批量查找可以使用 getAllPresent () 方法或者带填充默认值的 getAll () 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 手动填充测试
*/
public void cacheTest(String k) {
Cache<String, String> cache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterAccess(100L, TimeUnit.SECONDS)
.build();

cache.put("c1", "c1");
//获取缓存值,如果为空,返回null
log.info("cacheTest present: [{}] -> [{}]", k, cache.getIfPresent(k));
//获取返回值,如果为空,则运行后面表达式,存入该缓存
log.info("cacheTest default: [{}] -> [{}]", k, cache.get(k, this::buildLoader));
log.info("cacheTest present: [{}] -> [{}]", k, cache.getIfPresent(k));
//清除缓存
cache.invalidate(k);
log.info("cacheTest present: [{}] -> [{}]", k, cache.getIfPresent(k));
}
private String buildLoader(String k) {
return k + "+default";
}

同步

同步加载:LoadingCache 对象进行缓存的操作,使用 CacheLoader 进行缓存存储管理。批量查找可以使用 getAll () 方法。默认情况下,getAll () 将会对缓存中没有值的 key 分别调用 CacheLoader.load 方法来构建缓存的值(build 中的表达式)。我们可以重写 CacheLoader.loadAll 方法来提高 getAll () 的效率。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
* 同步填充测试
*/
public void loadingCacheTest(String k) {
//同步填充在build方法指定表达式
LoadingCache<String, String> loadingCache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterAccess(100L, TimeUnit.SECONDS)
.build(this::buildLoader);

loadingCache.put("c1", "c1");
log.info("loadingCacheTest get: [{}] -> [{}]", k, loadingCache.get(k));
//获取缓存值,如果为空,返回null
log.info("loadingCacheTest present: [{}] -> [{}]", k, loadingCache.getIfPresent(k));
//获取返回值,如果为空,则运行后面表达式,存入该缓存
log.info("loadingCacheTest default: [{}] -> [{}]", k, loadingCache.get(k, this::buildLoader));
log.info("loadingCacheTest present: [{}] -> [{}]", k, loadingCache.getIfPresent(k));
loadingCache.invalidate(k);
log.info("loadingCacheTest present: [{}] -> [{}]", k, loadingCache.getIfPresent(k));
}
private String buildLoader(String k) {
return k + "+default";
}

异步

自动异步加载:
AsyncLoadingCache 对象进行缓存管理,get () 返回一个 CompletableFuture 对象,默认使用 ForkJoinPool.commonPool () 来执行异步线程,但是我们可以通过 Caffeine.executor (Executor) 方法来替换线程池。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 异步填充测试
*/
public void asyncLoadingCacheTest(String k) throws ExecutionException, InterruptedException {
//异步加载使用Executor去调用方法并返回一个CompletableFuture。异步加载缓存使用了响应式编程模型。
//
//如果要以同步方式调用时,应提供CacheLoader。要以异步表示时,应该提供一个AsyncCacheLoader,并返回一个CompletableFuture。
AsyncLoadingCache<String, String> asyncLoadingCache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterAccess(100L, TimeUnit.SECONDS)
.buildAsync(s -> this.buildLoaderAsync("123").get());

log.info("asyncLoadingCacheTest get: [{}] -> [{}]", k, asyncLoadingCache.get(k).get());
//获取返回值,如果为空,则运行后面表达式,存入该缓存
log.info("asyncLoadingCacheTest default: [{}] -> [{}]", k, asyncLoadingCache.get(k, this::buildLoader).get());
}

private CompletableFuture<String> buildLoaderAsync(String k) {
return CompletableFuture.supplyAsync(() -> k + "+buildLoaderAsync");
}

手动异步加载:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 手动异步测试
*/
public void asyncManualCacheTest(String k) throws ExecutionException, InterruptedException {
//异步加载使用Executor去调用方法并返回一个CompletableFuture。异步加载缓存使用了响应式编程模型。
//
//如果要以同步方式调用时,应提供CacheLoader。要以异步表示时,应该提供一个AsyncCacheLoader,并返回一个CompletableFuture。
AsyncCache<String, String> asyncCache = Caffeine.newBuilder()
.maximumSize(100)
.expireAfterAccess(100L, TimeUnit.SECONDS)
.buildAsync();
//获取返回值,如果为空,则运行后面表达式,存入该缓存
log.info("asyncManualCacheTest default: [{}] -> [{}]", k, asyncCache.get(k, this::buildLoader).get());
}

过期策略

Caffeine 的缓存清除是惰性的,可能发生在读请求后或者写请求后,比如说有一条数据过期后,不会立即删除,可能在下一次读 / 写操作后触发删除(类比于 redis 的惰性删除)。如果读请求和写请求比较少,但想要尽快的删掉 cache 中过期的数据的话,可以通过增加定时器的方法,定时执行 cache.cleanUp () 方法(异步方法,可以等待执行),触发缓存清除操作。

  1. 基于大小过期:当缓存超出后,使用 W-TinyLFU 算法进行缓存淘汰处理

    • 基于缓存条目过期
      maximumSize () 方法,参数是缓存中存储的最大缓存条目,当添加缓存时达到条目阈值后,将进行缓存淘汰操作
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    /**
    * 淘汰策略-size
    */
    public void sizeTest() {
    LoadingCache<String, String> loadingCache = Caffeine.newBuilder()
    .maximumSize(1)
    .build(this::buildLoader);

    List<String> list = Lists.newArrayList("c1", "c2", "c3");
    loadingCache.put(list.get(0), list.get(0));
    log.info("weightTest get: [{}] -> [{}]", list.get(0), loadingCache.get(list.get(0)));
    loadingCache.put(list.get(1), list.get(1));
    log.info("weightTest get: [{}] -> [{}]", list.get(1), loadingCache.get(list.get(1)));
    loadingCache.put(list.get(2), list.get(2));
    log.info("weightTest get: [{}] -> [{}]", list.get(2), loadingCache.get(list.get(2)));
    log.info("weightTest cache map:{}", loadingCache.getAll(list));
    }
  • 基于权重过期:权重大小不会决定缓存满时清楚的优先级
    weigher () 方法可以指定缓存所占比重,maximumWeight () 方法指定最大的权重阈值,当添加缓存超过规定权重后,进行数据淘汰

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /**
    * 淘汰策略-weight
    */
    public void weightTest() {
    LoadingCache<String, String> loadingCache = Caffeine.newBuilder()
    .maximumWeight(10)
    .weigher((key, value) -> 5)
    .build(this::buildLoader);

    List<String> list = Lists.newArrayList("c1", "c2", "c3");
    loadingCache.put(list.get(0), list.get(0));
    log.info("weightTest get: [{}] -> [{}]", list.get(0), loadingCache.get(list.get(0)));
    loadingCache.put(list.get(1), list.get(1));
    log.info("weightTest get: [{}] -> [{}]", list.get(1), loadingCache.get(list.get(1)));
    log.info("weightTest cache map:{}", loadingCache.getAll(list));
    }
  1. 基于时间过期
    expireAfterAccess ():缓存访问后,一定时间失效;即最后一次访问或者写入开始时计时。
    expireAfterWrite ():缓存写入后,一定时间失效;以写入缓存操作为准计时。
    expireAfter ():自定义缓存策略,满足多样化的过期时间要求。

    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
    /**
    * 淘汰策略-time
    */
    public void timeTest() {
    //1.缓存访问后,一定时间后失效
    LoadingCache<String, String> loadingCacheOne = Caffeine.newBuilder()
    .expireAfterAccess(10L, TimeUnit.SECONDS)
    .build(this::buildLoader);

    //2.缓存写入后,一定时间后失效
    LoadingCache<String, String> loadingCacheTwo = Caffeine.newBuilder()
    .expireAfterWrite(10L, TimeUnit.SECONDS)
    .build(this::buildLoader);

    //3.自定义过期策略
    LoadingCache<String, String> loadingCacheThree = Caffeine.newBuilder()
    .expireAfter(new Expiry<Object, Object>() {
    @Override
    public long expireAfterCreate(@NonNull Object o, @NonNull Object o2, long l) {
    return 0;
    }

    @Override
    public long expireAfterUpdate(@NonNull Object o, @NonNull Object o2, long l, @NonNegative long l1) {
    return 0;
    }
    @Override
    public long expireAfterRead(@NonNull Object o, @NonNull Object o2, long l, @NonNegative long l1) {
    return 0;
    }
    })
    .build(this::buildLoader);
    }
  2. 基于引用过期

    引用名称 垃圾回收时机 用途 生存周期
    强引用(Strong Reference) 不回收 正常普遍的类别 JVM 停止运行
    软引用(soft Reference) 内存不足回收 缓存对象 内存不足时
    弱引用(weak Reference) 下一次 GC 回收 缓存对象 GC 之后
    虚引用(Phantom Reference) 随时回收,引用虚设 通知功能 随时回收

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    /**
    * 淘汰策略-引用
    */
    public void referenceTest() {
    //1.弱引用弱key方式,没有强引用时,回收
    LoadingCache<String, String> loadingCacheOne = Caffeine.newBuilder()
    .weakKeys()
    .weakValues()
    .build(this::buildLoader);

    //2.软值引用,内存不足的时候回收
    LoadingCache<String, String> loadingCacheTwo = Caffeine.newBuilder()
    .softValues()
    .build(this::buildLoader);
    }

    ​ Caffeine.weakKeys ():使用弱引用存储 key,当没有其他的强引用时,则会被垃圾回收器回收;
    ​ Caffeine.weakValues ():使用弱引用存储 value,当没有其他的强引用时,则会被垃圾回收器回收;
    ​ Caffeine.softValues ():使用软引用存储 key,当没有其他的强引用时,内存不足的时候会被回收;
    ​ 注:Weak or soft values can not be combined with AsyncCache

手动删除

Caffeine 中提供了手动进行缓存的删除,无需等待我们上面提到的被动的一些删除策略,使用方法如下:

1
2
3
cacheOne.invalidateAll();
cacheOne.invalidate(Object o);
cacheOne.invalidateAll(List);

事件监听

移除事件 RemovalListener 是一种缓存监听事件,当 key 被移除的时候就会触发这个方法,可以进行一些相关联的操作。RemovalListener 可以获取到 key、value 和 RemovalCause(删除的原因)。另外 RemovalListener 中操作是线程池异步执行的。

1
2
3
4
5
6
7
8
9
10
11
/**
* 移除监听器
*/
public void removeTest() throws InterruptedException {
Cache<String, String> cacheOne = Caffeine.newBuilder()
.maximumSize(1)
.removalListener(
(o, o2, removalCause) ->
System.out.println(o + " is " + "remove" + " reason is " + removalCause.name()))
.build();
}

参数总结

1
2
3
4
5
6
7
8
9
10
maximumSize:设置缓存最大条目数,超过条目则触发回收 
maximumWeight:设置缓存最大权重,设置权重是通过weigher方法, 需要注意的是权重也是限制缓存大小的参数,并不会影响缓存淘汰策略,也不能和maximumSize方法一起使用。
weakKeys:将key设置为弱引用,在GC时可以直接淘汰
weakValues:将value设置为弱引用,在GC时可以直接淘汰
softValues:将value设置为软引用,在内存溢出前可以直接淘汰
expireAfterWrite:写入后隔段时间过期
expireAfterAccess:访问后隔断时间过期
refreshAfterWrite:写入后隔断时间刷新
removalListener:缓存淘汰监听器,配置监听器后,每个条目淘汰时都会调用该监听器
writer:writer监听器其实提供了两个监听,一个是缓存写入或更新是的write,一个是缓存淘汰时的delete,每个条目淘汰时都会调用该监听器

Springboot 集成

spring 从 3 的版本开始支持 Cache 的处理,使用方式也是按照传统方向提供两种,基于注解和基于 xml 配置文件的方式,其操作纬度为方法或类,比如将注解 @Cacheable 按要求配置到一个方法上,会构造出一个 k-v 的缓存信息,k 是入参相关,value 就是方法的返回值,当请求到这个方法上,先去缓存获取,有则直接返回,无则执行流程然后返回且存入缓存。还有一些其他的注解,例如:@CachePut、@CacheEvict 等。

pom引入

1
2
3
4
5
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.8.0</version>
</dependency>

yml 文件配置

1
2
3
4
5
6
7
8
spring:
cache:
type: caffeine
cache-names:
- caffeineTestOne
- caffeineTestTwo
caffeine:
spec: initialCapacity=10,maximumSize=200,expireAfterWrite=3s

启动类加注解:@EnableCaching

1
2
3
4
5
6
7
8
@EnableCaching
@EnableOpenApi
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

主要注解

1
2
3
4
5
@Cacheable 触发缓存入口(这里一般放在创建和获取的方法上)
@CacheEvict 触发缓存的 eviction(用于删除的方法上)
@CachePut 更新缓存且不影响方法执行(用于修改的方法上,该注解下的方法始终会被执行)
@Caching 将多个缓存组合在一个方法上(该注解可以允许一个方法同时设置多个注解)
@CacheConfig 在类级别设置一些缓存相关的共同配置(与其它缓存配合使用)

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
@Cacheable(value = "user", key = "#customId")
public Custom getCustom(Integer customId) {
log.warn("通过数据库去查询,用户id为:{}", customId);
return customRepository.findById(customId)
.orElseThrow(() -> new UserNotFoundException("Users don't exist"));
}

@CacheEvict(value = "user", key = "#customId")
public void deleteCustom(Integer customId) {
Custom custom = customRepository.findById(customId)
.orElseThrow(() -> new UserNotFoundException("Users don't exist"));
customRepository.delete(custom);
}
  • Caffeine
FISCO根据私钥生成公钥及地址
ArrayList实现排序
  1. 1. 简介
  2. 2. 适用场景
  3. 3. 缓存填充策略
    1. 3.1. 手动
    2. 3.2. 同步
    3. 3.3. 异步
  4. 4. 过期策略
  5. 5. 手动删除
  6. 6. 事件监听
  7. 7. 参数总结
  8. 8. Springboot 集成
    1. 8.1. pom引入
    2. 8.2. yml 文件配置
    3. 8.3. 启动类加注解:@EnableCaching
    4. 8.4. 主要注解
  9. 9. 案例
© 2023 曳戈泰尔
Hexo Theme Yilia by Litten
  • 所有文章
  • 其他站点
  • 关于我

tag:

  • SHA256
  • AI换脸
  • VITS
  • APM
  • Anaconda
  • AnsibleAWX
  • ArrayList
  • Bean
  • BigDecimal
  • Blender three.js
  • API开放接口
  • xmlRPC
  • Ice
  • MySQL
  • BeanUtils
  • Tomcat
  • Caffeine
  • gradle
  • Docker
  • jdk
  • maven
  • JDK
  • localtime
  • Minio
  • PostgreSQL
  • RT_Linux
  • kafka
  • geany
  • CentOS
  • Elasticsearch
  • Node
  • FastDFS
  • Nginx
  • CompletableFutures
  • CompletableFuture
  • CountDownLatch
  • queue
  • Conflux
  • DefaultIdentifierGenerator
  • gdb
  • Deepin
  • Deferred
  • 自动化部署
  • Nacos
  • Redis
  • RocketMQ
  • docker-compose
  • docker日志
  • Docker部署
  • Drools
  • Vue
  • 持有者作为调用者
  • Error
  • ES
  • 签名上链
  • FISCO
  • Prometheus-Grafana-nodeExporter
  • FISCO开放平台
  • 解析input
  • ForkJoinPool
  • GateWay
  • Git
  • GeoServer
  • GitLab
  • Gradle
  • Spring
  • Gitlab
  • HTTP
  • Hexo GitHub-Pages
  • HttpUtil
  • IDEA
  • Gson
  • 热部署
  • HttpClientUtil
  • 搜索
  • Stream
  • foreach
  • Graphics2D
  • Synchronized
  • 循环
  • Integer
  • base64
  • JAVA
  • Excel
  • openID
  • NowTime
  • MD5
  • 字节流
  • 手机号
  • 支付宝
  • Object
  • 行政区划
  • 序列化
  • telnet
  • 枚举
  • 限流
  • 配置文件
  • 规则执行器
  • cmd
  • netty websocket
  • JAVE2
  • 线程池
  • 分治
  • JMH
  • JVM
  • Jenkins
  • Java调用AI输入输出
  • JWT
  • Kindle
  • Knif4j
  • jar
  • UDP
  • SonarQube
  • 部署
  • Ansible
  • IP
  • socket
  • List排序
  • MQ
  • MapStruct
  • Maven
  • MyBatis-Plus
  • MyBatis
  • 跳板机
  • event
  • trigger
  • 全文索引
  • 扣费 线程安全
  • MybatisPlus
  • LambdaQueryWrapper
  • Navicat
  • Explain
  • 私人助理
  • nacos
  • Nexus
  • WebSocket
  • OSS
  • OkHttpClient
  • OA
  • PicGo
  • 可视化监控
  • Optional
  • SpringBoot
  • 关键词
  • TSV
  • 性能指标
  • json
  • Pycharm
  • 文件夹遍历
  • TCP
  • Qt
  • QueryWrapper
  • Quartz
  • RSA
  • RabbitMQ
  • RateLimiter
  • Redisson
  • 阻塞等待锁
  • ZSET
  • incr
  • 频率限制
  • SAE
  • RESTful API
  • SCP
  • RuoYi
  • SM3
  • SKU
  • SQL
  • SQL前n天
  • SSL
  • Shell脚本自动化
  • Sleuth
  • Socket
  • PageHelper
  • Solidity
  • gateway
  • Batch
  • Spring Boot
  • build.gradle
  • Schedule
  • 循环重试
  • Undertow
  • 多筛选
  • IPFS
  • Jasypt
  • logback
  • 加解密
  • 幂等性
  • Result
  • log
  • Mail
  • 滑块
  • Druid
  • knife4j
  • 注入
  • Full Lite
  • 权限
  • 跨域访问
  • starter
  • 防刷
  • XSS
  • Event
  • 多数据库
  • Scheduled
  • yml
  • Async
  • AOP
  • CurrentUser
  • AutoGenerator
  • netty
  • Openfeign
  • Sentinel
  • Shiro
  • Swagger
  • XMLRPC
  • captcha
  • OAuth
  • 文件上传
  • 验证码登录
  • Response
  • Exception
  • 文件下载
  • 自定义注解
  • Thread
  • 观察者
  • 音视频
  • dll
  • StopWatch
  • String
  • Transactional
  • ThreadLocal
  • TLS
  • 挂载
  • VMware
  • VPN
  • VSAT
  • VScode
  • VS
  • Valid
  • 二维码
  • three.js
  • ECDSA
  • Tornado
  • WorkBench
  • VxWorks
  • select
  • taskSpawn
  • WPS
  • WeBase
  • JavaScript
  • COOKIE
  • 消息推送
  • 开启启动
  • VxWorks WorkBench
  • XStream
  • ab
  • appId appKey
  • printStackTrace
  • gitlab
  • excel2word
  • git
  • 经纬度
  • isNull isEmpty isBlank
  • mybatisplus
  • SSH
  • nohup日志
  • phpstudy
  • npm
  • 图片
  • nginx
  • url
  • xml
  • 去背景
  • 提取学号
  • 一键登录
  • xxl-job
  • 并发
  • 接口
  • 一致性HASH
  • 责任链
  • 两层请求体
  • 二次支付
  • 个人支付
  • 设计模式
  • 代理
  • MERGE
  • 保存MultipartFile
  • PDF
  • 链间数据迁移
  • session
  • 鉴权
  • 证书生成
  • 单例
  • 压测
  • shell
  • 发布Jar
  • sms
  • 升级代理合约
  • 支付
  • 图片转PDF
  • 拍平JSON
  • SSO
  • property
  • 内容安全
  • 循环分页
  • crontab
  • 日志清理
  • 实名
  • 绩效
  • 读书笔记
  • 歌词识别
  • component初始化
  • 抽奖
  • 数据脱敏
  • 验证码
  • 网络攻防
  • 慢排查
  • Native支付
  • 裁剪字符串
  • WebView
  • 文本折叠
  • 上拉加载数据
  • 弹窗输入框
  • 图片裁剪
  • banner
  • 局部刷新
  • 弹窗
  • StorageSync
  • 标签id值
  • openId
  • 角标
  • globalData
  • url传值
  • Feign
  • 懒加载
  • 订阅消息
  • 设备交接
  • 提取txt 提取word
  • 回调
  • 支付超时
  • Assert
  • 乐观锁
  • 服务器
  • 监控
  • 运维
  • 方案
  • Enum
  • 测试
  • 校招
  • 死锁
  • 兑换码
  • 订单
  • 余额扣减
  • sku gson
  • 电商
  • 短信验证码
  • 伏羲PDF
  • 秒杀
  • 后台
  • 不可破解key
  • 排查
  • 线程安全 Map List
  • 上下电
  • CRUD拆分的宽表
  • ip2region
  • 行政
  • 文件校验FileSize
  • ParameterMap
  • EventBus
  • 微信手机号
  • 购买掉单
  • resources
  • 音频时长
  • IDCardUtils
  • Ghelper
  • Forest
  • 邀请
  • 过滤器 拦截器
  • 通信
  • Retry
  • 人脸融合
  • 时间差
  • 短信
  • 集合
  • 长安链

    缺失模块。
    1、请确保node版本大于6.2
    2、在博客根目录(注意不是yilia根目录)执行以下命令:
    npm i hexo-generator-json-content --save

    3、在根目录_config.yml里添加配置:

      jsonContent:
        meta: false
        pages: false
        posts:
          title: true
          date: true
          path: true
          text: false
          raw: false
          content: false
          slug: false
          updated: false
          comments: false
          link: false
          permalink: false
          excerpt: false
          categories: false
          tags: true
    

  • 区块链
  • GPT
  • gitee1
  • gitee2
  • github
  • Other
徘徊在原子与字节的边缘

将更好的自己呈现给世界



修心至要,事上磨练

一蓑衣,一壶酒,一曲长歌,一剑天涯

见自己,见天地,见众生