本文介绍了如何可靠地从Guava LoadingCache删除记录?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用番石榴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 an AtomicLongMap in your ConcurrentHashMap and create a new one if not found (use putIfAbsent on Java 7 or computeIfAbsent on Java 8).
  • Using the name, you update the AtomicLongMap 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删除记录?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-13 07:41
查看更多