最新实战案例锦集:《Spring Boot3实战案例合集》持续更新,每天至少更新一篇文章,订阅后将赠送文章最后展示的所有MD文档(学习笔记)。
环境:Spring Boot3.2.5 + Guava
1. 简介
Guava 是 Google 开源的一个 Java 库,提供了一系列实用的功能和工具类,旨在简化常见的编程任务并提高代码质量和可维护性。Guava 包含了许多强大的功能,如集合扩展、缓存机制、并发工具、字符串处理、散列函数等。Guava的功能众多,本篇文章将只对缓存,布隆过滤器,限流做详细介绍。
缓存
Guava 提供了一个高性能的本地缓存机制,通过 Cache 类实现。Cache 支持自动过期、最大容量限制和加载策略等功能。它简化了缓存的管理和使用,适用于需要频繁访问且计算成本高的数据。
布隆过滤器
Guava 的 BloomFilter 是一种空间高效的概率数据结构,用于判断一个元素是否存在于集合中。它通过哈希函数将元素映射到一个位数组,支持快速的成员查询,但可能会有少量误报。布隆过滤器常用于大数据场景,如搜索引擎、数据库索引和网络爬虫,用于快速排除不存在的元素,减少不必要的计算和IO操作。
限流
Guava 提供了 RateLimiter 类来实现限流功能,通过令牌桶算法控制请求的速率。RateLimiter 可以限制每秒处理的请求数量,防止系统因突发流量而过载。它适用于需要控制访问频率的场景,如API调用、数据库访问和网络请求,确保系统稳定性和可用性。
接下来,我们将详细介绍以上3个功能在Spring Boot项目中的使用。
2. 实战案例
2.1 环境准备
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>33.3.1-jre</version>
</dependency>
当前使用的是最新版本(2024/11/09)。
2.2 缓存
缓存在各种使用场景中都非常有用。例如,当你需要多次获取某个输入的值,而该值的计算或检索成本很高时,你应该考虑使用缓存。
Guava Cache与 ConcurrentMap 相似,但又不完全相同。最根本的区别在于,ConcurrentMap 会持久保存所有添加到其中的元素,直到它们被明确删除。而Guava Cache 通常会自动驱逐条目,以限制内存占用。在某些情况下,即使不驱逐条目,LoadingCache 仍然很有用,因为它的自动缓存加载功能。如下示例:
LoadingCache<Long, User> cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(
new CacheLoader<Long, User>() {
public User load(Long key) {
System.out.println("从DB加载数据") ;
return new User(1L, "Pack", 33) ;
}
});
System.out.println(cache.get(1L)) ;
System.out.println(cache.get(1L)) ;
System.out.println(cache.get(1L)) ;
运行上面代码输出结果
从DB加载数据
User [id=1, name=Pack, age=33]
User [id=1, name=Pack, age=33]
User [id=1, name=Pack, age=33]
只有第一次会执行我们CacheLoader#load方法去加载数据,一旦加载后就会存于缓存中,后续直接从缓存中获取。
从Callable加载数据
当我们从缓存中获取数据时调用get方法,都可以传递第二个参数Callable,如下:
get(K, Callable<V>)
如果缓存中没有指定的key,则会调用Callable回调方法获取数据加载到缓存中,如下示例:
Cache<Long, User> cache = CacheBuilder.newBuilder()
.maximumSize(1000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build() ;
User ret = cache.get(1L, () -> {
System.out.println("从DB加载数据") ;
return new User(1L, "Pack", 33) ;
}) ;
System.out.println(ret) ;
ret = cache.get(1L, () -> {
System.out.println("从DB加载数据") ;
return new User(1L, "Pack", 33) ;
}) ;
System.out.println(ret) ;
输出结果
从DB加载数据
User [id=1, name=Pack, age=33]
User [id=1, name=Pack, age=33]
缓存中已经存在,所以第二次不会执行Callable回调。