java 设计一个带有可变入口过期的 Guava LoadingCache
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13979376/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Designing a Guava LoadingCache with variable entry expiry
提问by fge
I am using Guava's LoadingCache into my project to handle thread-{safe,friendly} cache loading and it works wonderfully well. However, there is a limitation.
我在我的项目中使用 Guava 的 LoadingCache 来处理线程{安全,友好}缓存加载,它运行得非常好。但是,有一个限制。
The current code defining the cache looks like this:
当前定义缓存的代码如下所示:
cache = CacheBuilder.newBuilder().maximumSize(100L).build(new CacheLoader<K, V>()
{
// load() method implemented here
}
I don't specify an expiry time.
我没有指定到期时间。
The problem is that according to the values of the key, some associated values may expire and others may not. And CacheLoader
does not account for this, if you specify an expiry time, it is for each and every entry.
问题在于,根据键的值,一些关联的值可能会过期,而另一些可能不会。并且CacheLoader
不考虑这一点,如果您指定到期时间,则是针对每个条目。
How would you tackle this problem?
你会如何解决这个问题?
回答by Jonathan
Another alternative is ExpiringMap, which supports variable entry expiration:
另一种选择是ExpiringMap,它支持变量条目过期:
Map<String, String> map = ExpiringMap.builder().variableExpiration().build();
map.put("foo", "bar", ExpirationPolicy.ACCESSED, 5, TimeUnit.MINUTES);
map.put("baz", "pez", ExpirationPolicy.CREATED, 10, TimeUnit.MINUTES);
回答by hoaz
I suggest you to include expiration time directly to your entry class and manually evict it from cache if it is expired immediately after you fetched it from cache:
我建议您将过期时间直接包含在您的条目类中,如果它在您从缓存中获取后立即过期,则手动将其从缓存中逐出:
MyItem item = cache.getIfPresent(key);
if (item != null && item.isExpired()) {
cache.invalidate(key);
item = cache.get(key);
// or use cache.put if you load it externally
}
As an alternative, I can suggest you to check EhCache library that supports per element expire policy.
作为替代方案,我建议您检查支持每个元素过期策略的 EhCache 库。
回答by Dimitris Andreou
LoadingCache
offers some commonly used expiration policies, but when these fall short for what you need, you need to roll your own.
LoadingCache
提供了一些常用的过期策略,但是当这些策略不能满足您的需求时,您需要推出自己的策略。
Just add a DelayQueue. Whenever you add something to the cache, add a Delayed
to that queue, with the appropriate expiration time. The Delayed
object should have a (weak?) reference to the key.
只需添加一个DelayQueue。每当您向缓存中添加内容时Delayed
,请向该队列添加一个,并具有适当的到期时间。该Delayed
对象应该有一个(弱?)对键的引用。
The final ingredient is that you need to periodically poll this queue, to see if something expired and has to be evicted. Don't necessarily add a thread to do this, you could just piggyback on whatever thread is accessing the LoadingCache
. Just before accessing the cache, eg:
最后一个要素是您需要定期轮询此队列,以查看某些内容是否已过期并必须被逐出。不一定要添加线程来执行此操作,您可以搭载任何正在访问LoadingCache
. 就在访问缓存之前,例如:
private void drainCache() {
MyDelayed expired;
while ((expired = delayedQueue.poll()) != null) {
K key = expired.getReference();
if (key != null) { // this only in case if you hold the key in a weak reference
loadingCache.invalidate(key);
}
}
}
..
V lookup(K key) {
drainCache();
return loadingCache.getUnchecked(key);
}
回答by brain storm
you could use caffeine library which is inspired by Guava. Here is an example usage from github repo
您可以使用受番石榴启发的咖啡因库。这是github repo的示例用法
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfterAccess(5, TimeUnit.MINUTES)
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfterCreate(5, TimeUnit.MINUTES)
回答by maaartinus
In case the entries are big and you need to conserve memory, I don't think there's a nice solution, but these hacks come in mind:
如果条目很大并且您需要节省内存,我认为没有很好的解决方案,但是请记住这些技巧:
Use a
PriorityQueue
ordered by the expiration time for manually removing the entries. If you want to be sure that no expired entry gets use, you need to combine this with the solution by hoaz; the queue only prevents the useless entries from occupying memory.You wrote "some associated values may expire and others may not", which suggest that the expiration delay is the same for all expiring entries. This would allow using a simpler and faster
Queue
(e.g.ArrayDeque
instead of thePriorityQueue
).In case the expiration delay is rather large, you could let all entries expire and reinsert those which should live forever in a
RemovalListener
. This can fail in two ways: 1. You may get a miss in the meantime. 2. The removals and re-inserts may cost a lot of CPU time.
使用
PriorityQueue
按到期时间排序来手动删除条目。如果您想确保没有过期条目被使用,您需要将其与 hoaz 的解决方案结合起来;队列只是防止无用的条目占用内存。你写了“一些相关的值可能会过期,而其他的可能不会”,这表明所有过期条目的过期延迟都是相同的。这将允许使用更简单和更快的
Queue
(例如ArrayDeque
代替PriorityQueue
)。如果过期延迟相当大,您可以让所有条目过期并重新插入那些应该永远存在于
RemovalListener
. 这可能会以两种方式失败: 1. 在此期间您可能会错过。2. 移除和重新插入可能会花费大量 CPU 时间。
回答by blomqvie
I guess you could use the explicit invalidate to defined exactly which entries should be evicted, but that's not probably what you want.
我想您可以使用显式无效来准确定义应该驱逐哪些条目,但这可能不是您想要的。
You can give different weights for entries though. It's not perfect, but you can guide the cache to evict entries which are less important. See Weighter, entries with weight 0 will not be evicted by size-based eviction.
不过,您可以为条目赋予不同的权重。这并不完美,但您可以引导缓存驱逐不太重要的条目。请参阅Weighter,权重为 0 的条目不会被基于大小的驱逐驱逐。