问题描述
我正在使用番石榴LoadingCache
向其中填充一些数据,我想每1分钟从该LoadingCache
中删除所有条目.
I am using Guava LoadingCache
to populate some data into it and I want to remove all the entries from that LoadingCache
every 1 minute.
public class MetricHolder {
private final ExecutorService executor = Executors.newFixedThreadPool(2);
private final LoadingCache<String, AtomicLongMap<String>> clientIdMetricCounterCache =
CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES)
.removalListener(RemovalListeners.asynchronous(new SendToDatabase(), executor))
.build(new CacheLoader<String, AtomicLongMap<String>>() {
@Override
public AtomicLongMap<String> load(String key) throws Exception {
return AtomicLongMap.create();
}
});
private static class Holder {
private static final MetricHolder INSTANCE = new MetricHolder();
}
public static MetricHolder getInstance() {
return Holder.INSTANCE;
}
private MetricHolder() {}
public void increment(String clientId, String name) throws ExecutionException {
clientIdMetricCounterCache.get(clientId).incrementAndGet(name);
}
public LoadingCache<String, AtomicLongMap<String>> getClientIdMetricCounterCache() {
return clientIdMetricCounterCache;
}
private static class SendToDatabase implements RemovalListener<String, AtomicLongMap<String>> {
@Override
public void onRemoval(RemovalNotification<String, AtomicLongMap<String>> notification) {
String key = notification.getKey();
AtomicLongMap<String> value = notification.getValue();
System.out.println(key);
System.out.println(value);
// sending these key/value to some other system
}
}
}
我正在从代码的许多不同位置以多线程方式调用increment
方法.因此,在1分钟的时间内,它将填充clientIdMetricCounterCache
中的许多指标.现在,我想每隔1分钟可靠地删除所有这些指标,并将所有这些指标发送到数据库.
I am calling increment
method from lot of different places in the code in a multithreaded way. So for a period of 1 minute it will populate lot of metrics in clientIdMetricCounterCache
. Now I want to drop all those metrics reliably after every 1 minute and send all those metrics to database.
就我而言,有时写入increment
方法的速度可能非常慢,但我仍然想每1分钟删除所有这些条目,并且我根本不对该高速缓存进行任何读取,只是对其进行写入,然后删除通过发送到其他系统来记录这些记录.以下是我在番石榴 Wiki
In my case, sometimes write to increment
method might be very slow but still I want to drop all those entries every 1 minute and I am not doing any read at all on this cache, just writing to it and then dropping those records by sending to some other system. Below is what I saw in the Guava wiki
那么expireAfterWrite
如何工作?它是否像调度程序那样工作,它将每1分钟运行一次并删除clientIdMetricCounterCache
中的所有条目,然后再次在1分钟后唤醒并从同一缓存中删除所有条目并继续运行?阅读完Wiki后,我怀疑它会那样工作.如果没有,那么我怎么能每隔1分钟可靠地删除这些记录并发送到其他系统,因为一段时间以来我的写操作很少见?
So how does expireAfterWrite
works? Does it work like a scheduler which will run every 1 minute and delete all the entries whatever is there in clientIdMetricCounterCache
and then again it will wake up after 1 minute and delete all the entries from the same cache and keep going like that? After reading the wiki, I doubt it works like that. If it doesn't, then how can I reliably drop those records every 1 minute and send to some other system as my writes can be rare for some time?
看起来我可能必须使用Guava TimeLimiter
接口和SimpleTimeLimiter
,或者可能是ScheduledExecutorService
才能可靠地使呼叫超时,然后删除条目?如果是,那么任何人都可以提供一个示例,该示例在我当前的示例中将如何工作?
Looks like I may have to use Guava TimeLimiter
interface and SimpleTimeLimiter
or may be ScheduledExecutorService
to reliably timeout the call and then drop the entries? If yes, can anyone provide an example how will this work in my current example?
推荐答案
在我看来,您似乎在滥用缓存,而Map正是这样做的.您没有使用到期时间,没有大小限制,没有没有缓存,只是在收集统计信息.
To me, it looks like you're misusing the cache, where a Map would do. You're using no expiration, no size limit, no caching, you're just gathering stats.
关于您正在使用的唯一功能是加载方面,这并不值得.
About the only feature you're using is the loading aspect, and that's not really worth it.
我建议改用AtomicReference<ConcurrentHashMap<String, AtomicLongMap>>
:
- 更新时,您可以通过
AtomicReference::get
获取当前分钟的版本. - 使用
clientId
,您在ConcurrentHashMap
中查找一个AtomicLongMap
,如果找不到则创建一个新的(在Java 7上使用putIfAbsent
或在Java 8上使用computeIfAbsent
). - 使用
name
,就像发布您一样更新AtomicLongMap
. - 每分钟通过
AtomicReference::getAndSet
替换所有内容.
- When updating, you get the version for the current minute via
AtomicReference::get
. - Using the
clientId
, you look up anAtomicLongMap
in yourConcurrentHashMap
and create a new one if not found (useputIfAbsent
on Java 7 orcomputeIfAbsent
on Java 8). - Using the
name
, you update theAtomicLongMap
just like you posted. - Once per minute you replace everything via
AtomicReference::getAndSet
.
通过替换,您可以确定自己的统计信息不会受到干扰,但是,您应该在getAndSet
之后稍等片刻,因为可能有些线程刚刚获得了引用并即将编写.
With the replacement, you can be sure that your stats don't interfere, however, you should wait a bit after getAndSet
as there may be threads who just obtained the reference and are about to write.
与原始方法相比,它将产生更多的垃圾,但是所有垃圾的寿命都将是短暂的,因此您实际上可以使GC更加满意.
It will produce more garbage than the original approach, but all the garbage will be short living, so you might actually make the GC more happy.
这很简单,不需要对库或其实现细节有深入的了解.
It's simple and need no deep knowledge of a library or its implementation details.
我猜想,volatile
代替AtomicReference
也可以.
这篇关于如何可靠地从Guava LoadingCache删除记录?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!