Guava库中的RateLimiter
和Cache
是两个不同的组件,分别用于控制访问频率和实现缓存功能。RateLimiter
用于流量控制,确保系统在处理请求时不会超过指定的速率,而Cache
则用于存储数据以加快访问速度。
由于RateLimiter
本身并不直接支持实现缓存功能,因此你无法直接使用RateLimiter
来实现实例级的本地缓存。但是,你可以结合Guava的Cache
和RateLimiter
来实现一个既具有缓存功能又受流量控制的系统。
以下是一个使用Guava的Cache
和RateLimiter
来实现实例级本地缓存的示例:
java
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.util.concurrent.RateLimiter;
import java.util.concurrent.TimeUnit;
public class CachedService {
// 创建一个缓存实例,设置缓存的最大容量为100,并且设置写入后5分钟过期
private Cache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(100)
.expireAfterWrite(5, TimeUnit.MINUTES)
.build();
// 创建一个RateLimiter实例,设置每秒允许2个请求
private RateLimiter rateLimiter = RateLimiter.create(2.0);
// 模拟从某个数据源获取数据的方法
private String fetchDataFromSource(String key) {
// 这里可以是数据库查询、HTTP请求等
return "Data for " + key;
}
// 获取数据的方法,首先尝试从缓存中获取,如果缓存中没有则通过RateLimiter控制后从数据源获取
public String getData(String key) {
// 尝试从缓存中获取数据
String cachedData = cache.getIfPresent(key);
if (cachedData != null) {
System.out.println("Cache hit for key: " + key);
return cachedData;
}
// 如果缓存中没有数据,则通过RateLimiter获取许可
rateLimiter.acquire(); // 这会阻塞当前线程直到获取到许可
// 尝试再次从缓存中获取数据(以防在获取许可期间有其他线程已经更新了缓存)
cachedData = cache.getIfPresent(key);
if (cachedData != null) {
return cachedData;
}
// 如果仍然没有数据,则从数据源获取
System.out.println("Fetching data from source for key: " + key);
String newData = fetchDataFromSource(key);
// 将新数据放入缓存中
cache.put(key, newData);
return newData;
}
public static void main(String[] args) {
CachedService service = new CachedService();
// 模拟多个线程同时访问数据
for (int i = 0; i < 10; i++) {
new Thread(() -> {
String key = "exampleKey";
String data = service.getData(key);
System.out.println("Thread " + Thread.currentThread().getId() + " got data: " + data);
}).start();
}
}
}
在这个示例中,CachedService
类有一个Cache
实例用于存储数据,并且有一个RateLimiter
实例用于控制对数据源的访问频率。getData
方法首先尝试从缓存中获取数据,如果缓存中没有数据,则通过RateLimiter
获取许可,然后再次检查缓存(因为可能在获取许可期间有其他线程已经更新了缓存),如果仍然没有数据,则从数据源获取并更新缓存。
请注意,这个示例中的RateLimiter
是全局的,意味着它会限制所有对getData
方法的调用。如果你想要对每个不同的键或每个不同的实例有独立的流量控制,你需要为每个键或实例创建单独的RateLimiter
实例。然而,这通常不是推荐的做法,因为它可能会增加复杂性和资源消耗。相反,你应该根据业务需求和系统架构来选择合适的流量控制策略。