# caffeine-study **Repository Path**: wulwu/caffeine-study ## Basic Information - **Project Name**: caffeine-study - **Description**: 高性能本地缓存——caffeine学习 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 3 - **Forks**: 4 - **Created**: 2020-12-22 - **Last Updated**: 2023-03-01 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # caffeine-study #### 介绍 caffeine:高性能本地缓存。[官方文档](https://github.com/ben-manes/caffeine/wiki/Benchmarks)
特点:高性能,高命中率,低内存占用。 #### 前提摘要 caffeine 基于 **W-TinyLFU** 设计,包含三种加载方法和一个过期策略。 1. 加载方法(手动、同步、异步) ```java 1.手动 Cache cache = Caffeine.newBuilder().build(); 2.同步 //默认的数据加载实现,当调用get取值的时候,如果key没有对应的值,就调用自定义方法 getValue 进行加载 LoadingCache cache = Caffeine.newBuilder().build(k -> getValue(key)); 3.异步 //AsyncLoadingCache是继承自LoadingCache类的,异步加载使用Executor去调用方法并返回一个CompletableFuture。异步加载缓存使用了响应式编程模型。 AsyncLoadingCache cache = Caffeine.newBuilder().buildAsync(k -> getAsyncValue(key).get()); ``` 2. 过期策略 ```java 1. 基于大小的过期方式 基于大小的回收策略有两种方式:一种是基于缓存大小,一种是基于权重。 // 根据缓存的计数进行驱逐 LoadingCache cache = Caffeine.newBuilder() .maximumSize(10000) .build(key -> function(key)); // 根据缓存的权重来进行驱逐(权重只是用于确定缓存大小,不会用于决定该缓存是否被驱逐) LoadingCache cache1 = Caffeine.newBuilder() .maximumWeight(10000) .weigher(key -> function1(key)) .build(key -> function(key)); maximumWeight与maximumSize不可以同时使用。 2.基于时间的过期方式 // 基于固定的到期策略进行退出 LoadingCache cache = Caffeine.newBuilder() .expireAfterAccess(5, TimeUnit.MINUTES) .build(key -> function(key)); LoadingCache cache1 = Caffeine.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) .build(key -> function(key)); // 基于不同的到期策略进行退出 LoadingCache cache2 = Caffeine.newBuilder() .expireAfter(new Expiry() { @Override public long expireAfterCreate(String key, Object value, long currentTime) { return TimeUnit.SECONDS.toNanos(seconds); } @Override public long expireAfterUpdate(@Nonnull String s, @Nonnull Object o, long l, long l1) { return 0; } @Override public long expireAfterRead(@Nonnull String s, @Nonnull Object o, long l, long l1) { return 0; } }).build(key -> function(key)); Caffeine提供了三种定时驱逐策略: expireAfterAccess(long, TimeUnit):在最后一次访问或者写入后开始计时,在指定的时间后过期。假如一直有请求访问该key,那么这个缓存将一直不会过期。 expireAfterWrite(long, TimeUnit): 在最后一次写入缓存后开始计时,在指定的时间后过期。 expireAfter(Expiry): 自定义策略,过期时间由Expiry实现独自计算。 缓存的删除策略使用的是惰性删除和定时删除。这两个删除策略的时间复杂度都是O(1)。 3. 基于引用的过期方式 Java中四种引用类型 引用类型 被垃圾回收时间 用途 生存时间 强引用 Strong Reference 从来不会 对象的一般状态 JVM停止运行时终止 软引用 Soft Reference 在内存不足时 对象缓存 内存不足时终止 弱引用 Weak Reference 在垃圾回收时 对象缓存 gc运行后终止 虚引用 Phantom Reference 从来不会 可以用虚引用来跟踪对象被垃圾回收器回收的活动,当一个虚引用关联的对象被垃圾收集器回收之前会收到一条系统通知 JVM停止运行时终止 // 当key和value都没有引用时驱逐缓存 LoadingCache cache = Caffeine.newBuilder() .weakKeys() .weakValues() .build(key -> function(key)); // 当垃圾收集器需要释放内存时驱逐 LoadingCache cache1 = Caffeine.newBuilder() .softValues() .build(key -> function(key)); 注意:AsyncLoadingCache不支持弱引用和软引用。 Caffeine.weakKeys(): 使用弱引用存储key。如果没有其他地方对该key有强引用,那么该缓存就会被垃圾回收器回收。由于垃圾回收器只依赖于身份(identity)相等,因此这会导致整个缓存使用身份 (==) 相等来比较 key,而不是使用 equals()。 Caffeine.weakValues() :使用弱引用存储value。如果没有其他地方对该value有强引用,那么该缓存就会被垃圾回收器回收。由于垃圾回收器只依赖于身份(identity)相等,因此这会导致整个缓存使用身份 (==) 相等来比较 key,而不是使用 equals()。 Caffeine.softValues() :使用软引用存储value。当内存满了过后,软引用的对象以将使用最近最少使用(least-recently-used ) 的方式进行垃圾回收。由于使用软引用是需要等到内存满了才进行回收,所以我们通常建议给缓存配置一个使用内存的最大值。 softValues() 将使用身份相等(identity) (==) 而不是equals() 来比较值。 Caffeine.weakValues()和Caffeine.softValues()不可以一起使用。 ``` #### 背景 当前场景:需要缓存 字典码、工作流、状态机等基本上不变动且不强要求一致性的前提下,我的选型是手动加载(通过get方法实现LodingCache效果) + 基于大小的过期方式&&基于时间的过期方式
后期会在此缓存架构上增加 二级甚至三级缓存,来达到分布式集群部署要求,本服务没有增加强一性代码,可以在新增/修改/删除db时同步put缓存。 ```java //手动填充 --> 同步填充 try { //已并发压测 500线程,这种写法 == LocalCache Object value = CacheUtil.cacheManager.get(actionCode.toString(), k -> createExpensiveGraph(k)); return (OrderActionCode) value; }catch (Exception e) { e.printStackTrace(); } ``` #### 使用说明 1. 安装配置 jdk1.8+ | tomcat | maven 环境 (idea) 2. 安装 git 3. git clone https://gitee.com/wulwu/caffeine-study.git 4. 进入目录文件 mvn clean install 5. 启动 java -jar xx.jar &