本地缓存Caffeine的坑,我是如何一步步爬出来的
发布时间:2025-05-22 08:50:33 发布人:远客网络
一、本地缓存Caffeine的坑,我是如何一步步爬出来的
1、在软件开发的优化策略中,缓存技术的应用不可或缺。小义在尝试构建一个基于Caffeine和Redis的多级缓存组件时,不幸陷入了一些本地缓存Caffeine的陷阱。本文将带您了解我如何逐步解决这些问题。
2、首先,本地缓存作为应用性能的第一道防线,选择合适的解决方案至关重要。常见的选项有Ehcache,它以纯Java实现,提供了丰富的策略如LRU和FIFO。Guava,由Google开发,其简洁的缓存模块支持自定义驱逐策略,比如大小和时间限制。
3、小义原本看中Caffeine,它专为Java 8设计,利用并发特性,提供高性能。Caffeine的W-TinyLFU算法结合了LRU和LFU的优点,且过期时间设置满足需求。然而,初次实践时,Caffeine的默认配置限制了灵活性,比如它无法按需为每个键设置个性化的过期时间,无法直接查看缓存项剩余时间,这与Redis的TTL命令不同。
4、幸运的是,Caffeine支持自定义缓存对象,通过expireAfter方法可以解决这个问题。我们需要在缓存对象类中添加过期时间和创建时间属性,并在自定义Caffeine缓存时,针对每个key设置独特的过期时间,以实现类似Redis的功能。
5、通过这些步骤,我逐步解决了Caffeine的坑,并实现了多级缓存组件中本地缓存部分的个性化过期时间管理。虽然过程艰辛,但最终结果满足了项目需求,提高了应用性能。
二、学习使用本地缓存Caffeine
1、Caffeine是一个基于Java 8的高性能缓存库。其内部实现基于Java进程内存,提供多种构建策略生成Cache容器,包括Cache、LoadingCache、AsyncCache和AsyncLoadingCache实例的构造器(Builder)。默认情况下,Caffeine创建的缓存实例不执行任何类型回收。
2、Caffeine的Cache实例被实现为哈希表,具有与ConcurrentHashMap相似的性能特征。其asMap视图及其集合视图具有弱一致性迭代器,安全地在并发使用,但如果其他线程在迭代器创建之后修改缓存,这些修改在迭代器中是否反映出来是不确定的。
3、默认情况下,返回的缓存使用equals方法来确定键或值的相等。如果指定了weakKeys,则缓存对键使用identity比较。同样,如果指定了weakValues或softValues,缓存将对值进行标识比较。
4、当应用maximumSize、maximumWeight、expireAfter、expireAfterWrite、expireAfterAccess、weakKeys、weakValues或softvalue时,缓存项在符合条件时将自动从缓存中删除。使用maximumSize或maximumWeight时,缓存项可能在每次缓存修改时被清除。expireAfter、expireAfterWrite或expireAfterAccess应用时,缓存项可能在每次缓存修改、偶尔的缓存访问或调用Cache.cleanUp时被清除。Caffeine支持使用Scheduler及时删除过期缓存项,而不是等待定期维护。
5、如果使用弱键、弱值或软值,缓存中存在的键或值可能会被垃圾收集器回收。带有回收键或值的缓存项在每次缓存修改、偶尔的缓存访问或调用cache. cleanup时从缓存中删除。缓存的估计大小计算包括这些项,但对读写操作来说是不可见的。
6、某些缓存配置可能导致定期维护任务的积累,这些任务在写操作期间执行,或在没有写操作的情况下,偶尔的读操作也可能导致执行。Cache实例的Cache.cleanUp方法将执行维护。只有使用maximumSize、maximumWeight、expireAfter、expireAfterWrite、expireAfterAccess、weakKeys、weakvalue或softvalue构建的缓存才能进行定期维护。
7、Caffeine生成的缓存是可序列化的,反序列化的缓存保留原始缓存的所有配置属性。请注意,序列化表单不包括缓存内容,只包括配置。
8、开发者建议:不要依赖缓存过期时间来实现业务逻辑。就像JVM的垃圾回收机制触发具有不确定性一样,Caffeine的回收策略也是复杂且不确定的。可以假定设置了淘汰机制的缓存项将在未来的某段时间将过期,但不能确定究竟在什么时间点、什么操作发生前被回收,除非主动操作删除。
9、Caffeine的方法包括newBuilder、initialCapacity、maximumSize、expireAfterWrite、expireAfterAccess、refreshAfterWrite、build、removalListener和writer。newBuilder构造默认设置的Caffeine实例。initialCapacity设置内部数据结构的最小容量。maximumSize指定缓存可以包含的最大缓存项数量。expireAfterWrite和expireAfterAccess自动从缓存中删除缓存项。refreshAfterWrite在创建后一段时间内若没有被写入,重新构建(refresh)缓存项。build构建缓存容器。removalListener指定一个监听器在每次删除缓存项时接受通知。writer指定写入器在创建或修改缓存项时通知该写入器。
10、Caffeine的容器接口包括Cache、LoadingCache、AsyncCache和AsyncLoadingCache。Cache键到值的半持久化映射,手动添加缓存项,直到清除或手动使其失效为止。LoadingCache键到值的半持久化映射,值由缓存自动加载,直到删除或手动使其失效为止。AsyncCache键到值的半持久化映射,手动添加缓存项,直到清除或手动使其失效为止。AsyncLoadingCache键到值的半持久化映射,缓存自动异步加载值,直到删除或手动使其失效为止。
11、Cache容器方法包括get、getIfPresent、put和invalidate。get返回与键相关联的值,必要时从CacheLoader.load(Object)获取。getIfPresent返回与此缓存中的键关联的值,如果没有键的缓存值,则返回null。put将值与此缓存中的键关联,替换旧值。invalidate丢弃键的任何缓存值。cleanUp执行任何当前有必要执行的维护操作。
12、在Spring boot maven项目中,引入Caffeine依赖。编写测试案例,运行结果表明,Caffeine设置最大缓存数量后,读取时删除缓存项,命中数略大于预设值,因为清理是通过定期维护任务触发的。增大读写数量,测试并发达到每秒2000万次。
13、在CacheTest中演示了Caffeine的使用,设计为全局资源的单例模式,使用Spring框架的Bean注入,以在程序进程间共享。在并发环境下,Cache容器的使用显著降低了连续数据库请求的频率,穿透访问数降低。测试请求返回结果每分钟变化一次,验证了缓存效果。
14、进程级缓存是解决高并发问题的手段之一,在分布式环境中,还需结合Redis等分布式缓存技术。
三、25个小众的Java库
1、庆祝Java的25周年纪念,本文将为您介绍25个实用的Java库,这些库均是成熟且久经考验的,能够解决Java软件开发中常见的问题。
2、RxJava,一种流行用于处理异步和事件驱动编程的软件开发范例,提供流畅且干净的API,帮助开发者以声明方式在事件/数据序列上添加运算符,轻松扩展观察者模式以支持事件驱动编程。
3、OkHttp,一个简单而强大的HTTP库,支持连接池、GZIP压缩、响应缓存等高级特性,提供流畅的API用于在Java中开发HTTP客户端。
4、MyBatis,用于处理SQL存储的优秀库,能够直接使用JDBC并编写SQL查询,或通过注释和XML描述符映射Java对象到存储过程或SQL语句,提供灵活的映射方式。
5、HikariCP,一个轻量级的数据库连接池,可以有效重用连接,避免创建新连接带来的性能影响,提高应用程序性能。
6、Lombok,简化Java中的样板代码生成,通过注解自动生成getter、setter、hashcode、equals、toString等方法,减少开发人员的编码工作。
7、VAVR,弥补Java在函数式编程功能上的不足,提供持久集合、错误处理的函数式抽象、并发编程、模式匹配等特性。
8、Gson,Google提供的简约JSON处理库,提供数据绑定、通用支持和灵活的自定义,无需注解即可轻松处理JSON数据。
9、jsoup,用于处理实际HTML的Java库,提供方便的API用于获取URL、提取和处理数据,支持从URL或字符串解析HTML。
10、JIB,简化Java应用程序容器化过程的开源工具,帮助开发者构建优化的Docker和OCI镜像。
11、Tink,Google开发的密码库,提供易于使用但不易误用的安全API,支持对称密钥加密、数字签名等多种加密功能。
12、Webmagic,一个全面的Web爬虫库,涵盖了爬虫的整个生命周期,提供简单、灵活的核心、多线程支持和易于使用的API。
13、ANTLR 4,强大的解析器生成器,用于读取、处理、执行或翻译结构化文本或二进制文件,简化数据处理过程。
14、Caffeine,高性能、近乎最佳的Java缓存库,提供流畅的缓存API和高级特性,如异步加载项和异步刷新。
15、Metrics,提供对Java应用程序关键组件监控的库,包括事件发生率、挂起的作业、服务运行状况检查等指标。
16、gRPC-Java,gRPC客户端的Java实现,简化在Java中使用gRPC的过程,提供高效和可靠的远程过程调用。
17、Java WebSocket,Java实现的WebSocket服务器和客户端,用于实现双向通信协议,适用于现代客户端-服务器通信场景。
18、JJWT,用于创建和验证JWT的简单Java库,完全符合RFC规范,提供易于阅读和使用的API。
19、Swagger-Core,OpenAPI规范的Java实现,自动为Java或JavaEE应用程序生成REST API文档。
20、Async Http Client,支持异步HTTP响应处理的流行Java库,同时支持WebSocket协议,提高应用程序性能。
21、Liquibase,用于数据库更改跟踪、版本控制和部署的工具,简化SQL数据库迁移过程。
22、Springfox,从源代码自动生成REST API文档的库,适用于基于Spring的Java应用程序。
23、JavaCV,封装计算机视觉领域中OpenCV和其他流行库的Java库,提供高级功能,如全屏图像显示、并行执行代码等。
24、Joda Time,提供高级日期和时间功能的库,适用于旧版本Java(Java8之前),但新版本Java已内置这些功能。
25、Wiremock,REST API的模拟器,帮助开发人员在编写单元测试期间模拟服务,提高基于微服务开发的开发速度。
26、MapStruct,代码生成器,根据配置方法上的约定在POJO之间实现映射,简化POJO/Bean转换过程。
27、本文列举的Java库涵盖了多种用途和场景,从数据处理、Web开发、数据库管理到安全性和测试,帮助Java开发者提升开发效率和解决问题。