首页 > 其他分享 >Caffeine高性能本地缓存框架初探

Caffeine高性能本地缓存框架初探

时间:2023-03-07 11:00:25浏览次数:52  
标签:返回 缓存 log cache Caffeine 初探 import main

通常情况下,为了提升服务性能,使用缓存框架是一个非常常见的选择。在Java语境下,经过我查阅,Caffeine被称作地标最强Java本地缓存框架。Caffeine是站在巨人(Guava Cache)的肩膀上,优化了算法发展而来。

在之前的性能测试框架开发中,通常用的缓存的时候都直接用java.util.concurrent.ConcurrentHashMap,但一涉及到过期策略就有点难以为继,搞不定了。经过简单学习实践,也算是Caffeine入门了。下面分享一下学习成果。

简介

Caffeine是Java语言的本地缓存性能框架,兼容Groovy语言,其他各位可以自行搜索。

常用功能

我主要用到Caffeine功能3点:

  1. 灵活的过期策略,可以访问计时过期、写入计时过期、自定义
  2. 灵活的写入策略,可以手动,还能同步,还可以异步
  3. API简单,上手快

其他高级功能暂时用不到,Caffeine性能数据,下次我单独JMH测试一下。

功能演示

主要实践3中写入策略的实践,过期策略其实只用前两种(访问、写入)即可满足现在的需求。

手动写入

import com.funtester.frame.SourceCode
import com.github.benmanes.caffeine.cache.Cache
import com.github.benmanes.caffeine.cache.Caffeine
import groovy.util.logging.Log4j2

import java.util.concurrent.TimeUnit
import java.util.function.Function

@Log4j2
class CaffeineManual extends SourceCode {

    static void main(String[] args) {
        Cache<Integer, Integer> cache = Caffeine.newBuilder()
                .maximumSize(100)
                .expireAfterWrite(100, TimeUnit.MILLISECONDS)
                .recordStats()
                .build()

        int key = 1
        log.info("无缓存返回: {}", cache.getIfPresent(key))
        log.info("无缓存自定义返回: {}", cache.get(key, new Function<Integer, Integer>() {

            @Override
            Integer apply(Integer integer) {
                return 3
            }
        }))
        cache.put(key, 2)
        log.info("手动赋值后返回: {}", cache.getIfPresent(key))
        sleep(1.0)
        log.info("缓存过期返回: {}", cache.getIfPresent(key))
        cache.put(key, 2)
        cache.invalidate(key)
        log.info("手动删除后返回: {}", cache.getIfPresent(key))
    }
}

控制台打印:

21:41:30.329 main 无缓存返回: null
21:41:30.337 main 无缓存自定义返回: 3
21:41:30.338 main 手动赋值后返回: 2
21:41:31.360 main 缓存过期返回: null
21:41:31.364 main 手动删除后返回: null

同步写入

import com.funtester.frame.SourceCode
import com.github.benmanes.caffeine.cache.CacheLoader
import com.github.benmanes.caffeine.cache.Caffeine
import com.github.benmanes.caffeine.cache.LoadingCache
import groovy.util.logging.Log4j2

import java.util.concurrent.TimeUnit

@Log4j2
class CaffeineSync extends SourceCode {

    static int cacheInit(int key) {
        log.info("返回赋值: {}", key)
        return key * 100;
    }

    static void main(String[] args) {
        LoadingCache<Integer, Integer> cache = Caffeine.newBuilder()
                .expireAfterWrite(1, TimeUnit.MINUTES)
                .maximumSize(100)
                .build(new CacheLoader<Integer, Integer>() {

                    @Override
                    Integer load(Integer integer) throws Exception {
                        return cacheInit(integer)
                    }
                });

        Integer value = cache.get(1)
        log.info("无缓存返回: {}", value)
        log.info("自定义返回: {}", cache.get(2, {
            return 31
        }))
        log.info("获取返回结果: {}", value)
        Map<Integer, Integer> resMap = cache.getAll([1, 2, 3])
        log.info("批量返回: {}",resMap)
    }
}

控制台打印:

21:54:54.900 main 返回赋值: 1
21:54:54.903 main 无缓存返回: 100
21:54:54.963 main 自定义返回: 31
21:54:54.963 main 获取返回结果: 100
21:54:54.964 main 返回赋值: 3
21:54:54.965 main 批量返回: {1=100, 2=31, 3=300}

这里可以看到,自定义返回时,自定义的数值是优先于CacheLoader中的加载方法的。经过我测试,当自定义闭包里面如果报错的话,当前线程会中断。这时候可以用try-catch语法返回一个null即可。

异步加载


import com.funtester.frame.SourceCode
import com.funtester.frame.execute.ThreadPoolUtil
import com.github.benmanes.caffeine.cache.AsyncCache
import com.github.benmanes.caffeine.cache.Caffeine
import groovy.util.logging.Log4j2

import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
import java.util.function.Function

@Log4j2
class CaffeineAsync extends SourceCode {

    static int cacheInit(int key) {
        return key * 100
    }

    static void main(String[] args) {
        AsyncCache<Integer, Integer> asyncCache = Caffeine.newBuilder()
                .expireAfterWrite(1, TimeUnit.SECONDS)
                .maximumSize(100).executor(ThreadPoolUtil.getFunPool()).buildAsync()
        CompletableFuture<Integer> future = asyncCache.get(1, new Function<Integer, Integer>() {

            @Override
            Integer apply(Integer integer) {
                log.info("开始加载缓存")
                sleep(1.0)
                return cacheInit(integer)
            }
        })
        log.info("FunTester1")
        sleep(2.0)
        log.info("FunTester2")
        log.info("异步加载返回: {}", future.get())
        sleep(2.0)
        log.info("缓存过期后Future返回: {}", future.get())
        log.info("缓存过期后cache返回: {}", asyncCache.getIfPresent(1))
        log.info("无缓存返回: {}", asyncCache.getIfPresent(2))
    }
}

控制台打印:

22:13:06.728 main FunTester1
22:13:06.728 F-1  开始加载缓存
22:13:08.738 main FunTester2
22:13:08.747 main 异步加载返回: 100
22:13:10.748 main 缓存过期后Future返回: 100
22:13:10.749 main 缓存过期后cache返回: null
22:13:10.750 main 无缓存返回: null

这里我们看2个信息:

  1. 加载程序是在CompletableFuture执行get之前完成的。
  2. 缓存过期之后,CompletableFuture还是可以获取值的。但是asyncCache.getIfPresent(1)返回值就是null了。

关于Caffeine功能的实践就到这里了,基本上就是半小时之内上手。这里友情提醒一下,Caffeine最新版本不支持JDK8了,目前我使用JDK8的Caffeine版本信息如下:

    compile group: 'com.github.ben-manes.caffeine', name: 'caffeine', version: '2.9.3'

标签:返回,缓存,log,cache,Caffeine,初探,import,main
From: https://blog.51cto.com/FunTester/6105371

相关文章

  • discuz关闭缓存
    修改 function_core.php文件vimdiscuz_x3.2/upload/source/function/function_core.php代码如下,添加红色字体functionchecktplrefresh($maintpl,$subtpl,$timecompar......
  • 浏览器数据库indexeddb-数据库缓存
    随着浏览器的功能不断增强,越来越多的网站开始考虑,将大量数据储存在客户端,这样可以减少从服务器获取数据,直接从本地获取数据。现有的浏览器数据储存方案,都不适合储存大量数......
  • redis缓存过期策略学习
    转自:http://jinguoxing.github.io/redis/2018/10/10/redis-EXPIRE/https://juejin.cn/post/70589185032559001671.过期字典在redis中维护了一个expires字典,里面保存了数......
  • spring的循环依赖和三级缓存
    一、spring中bean的生命周期回顾要理解spring中的循环依赖问题需要先了解spring中bean的生命周期,spring中创建bean的过程中主要有这几个阶段:实例化前$\rightarrow$实......
  • 07-Redis系列之-双写一致性,缓存详解和优化点
    双写一致性redis和mysql数据同步方案先更新数据库,再更新缓存(一般不用)先删缓存,再更新数据库(在存数据的时候,请求来了,缓存不是最新的,一般也不用)先更新数据库,再删缓存(请求......
  • 初探富文本之CRDT协同实例
    初探富文本之CRDT协同实例在前边初探富文本之CRDT协同算法一文中我们探讨了为什么需要协同、分布式的最终一致性理论、偏序集与半格的概念、为什么需要有偏序关系、如何通......
  • mybatis缓存
    一级缓存  二级缓存   二级缓存相关配置  mybatis缓存查询的顺序 ......
  • caffeine 高效缓存用法小记
    caffeine高效缓存用法小记。1.pom<dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId><vers......
  • 缓存穿透、雪崩、击穿简单认识
    缓存穿透原因:同一时间大量请求访问缓存中不存在的数据,因为缓存中没有,所以大量请求直接进入数据库,但是数据库中也没有,导致一时间数据库请求量过大!处理办法:布隆过滤器......
  • 深入理解webpack的chunkId对线上缓存的思考(转载)
    转载自 https://juejin.cn/post/6844903924818771981#heading-6 作者:Kimm想必经常使用基于webpack打包工具的框架的同学们,无论是使用React还是Vue在性能优化上使用最......