我有一个用expireAfterWrite(20, TimeUnit.MINUTES)
创建的Guava缓存。创建时,缓存也会使用数十个条目进行初始化。当值到期时,客户端应该调用外部服务并刷新缓存。
问题是 - 所有初始化的值会在20分钟后同时到期,客户端几乎会在同一个实例上调用该服务几十次。
我不希望这种情况发生。为了避免这种情况 - 一个想法是probabilistically expire the entries比TTL早一点,这样服务就不会同时受到很多打击。
不幸的是,我没有看到使用Guava缓存执行此操作的选项 - 至少没有从Wiki或Javadoc弹出任何内容。还有其他图书馆吗?我试图避免为此目的编写自己的缓存实现。
答案 0 :(得分:2)
缓存变暖是一个相当复杂的话题,所以有很多合理的方法,但不一定是单一的“正确”方法。一个简单的选择是在未来的交错时间简单地触发额外的写入,以便重置到期时间:
public static <K, V> void staggerCacheExpiration(
Cache<K, V> cache, long maxExpiration, TimeUnit unit, ScheduledExecutorService scheduler) {
for (Entry<K, V> e : cache.asMap().entrySet()) {
long delay = ThreadLocalRandom.current().nextLong(0, maxExpiration);
scheduler.schedule(() -> cache.put(e.getKey(), e.getValue()), delay, unit);
}
}
依赖于Cache
本身的任何方法的一个关键问题是,只有当调用者需要该值时,才会最终触发(可能很昂贵的)刷新。使用expireAfterWrite()
或refreshAfterWrite()
可能更好 ,而是运行一个专用线程,负责按顺序更新每个密钥。通过让单个线程更新所有密钥,您自然会避免同时刷新多个密钥的任何热点,并且还可以避免阻止依赖缓存中的值的任何线程。
正如Ben Manes建议您可能更愿意将Cache
封装在您自己的类型中,因此您选择的任何行为都不会暴露给您的来电者。