我有一个使用 expireAfterWrite(20, TimeUnit.MINUTES) 创建的 Guava 缓存
。缓存在创建时也会使用数十个条目进行初始化。当值过期时,客户端应该调用外部服务并刷新缓存。
问题是 - 所有初始化值都会在 20 分钟后同时过期,并且客户端几乎会在同一个实例中调用该服务数十次。
我不希望这种事发生。为了避免这种情况 - 一个想法是 probabilistically expire the entries比 TTL 早一点,这样服务就不会同时受到大量攻击。
不幸的是,我没有看到使用 Guava 缓存执行此操作的选项 - 至少 Wiki 或 Javadoc 中没有弹出任何内容。还有其他可用的图书馆吗?我试图避免为此目的编写自己的缓存实现。
最佳答案
缓存预热是一个相当复杂的主题,因此有很多合理的方法,但不一定是一种“正确”的方法。一个简单的选择是在未来的交错时间触发额外的写入,以重置到期时间:
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
封装在您自己的类型中,这样您选择的任何行为都不会暴露给调用者。
关于java - Guava Cache 的概率提前过期,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39927094/