0

我正在使用 Guava将一些数据填充到其中,并且我想每 1 分钟LoadingCache从中删除所有条目。LoadingCache

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 分钟后可靠地删除所有这些指标,并将所有这些指标发送到数据库。

就我而言,有时写入increment方法可能非常慢,但我仍然想每 1 分钟删除一次所有这些条目,而且我根本没有在这个缓存上进行任何读取,只是写入它,然后通过发送到一些删除这些记录其他系统。以下是我在 Guava wiki中看到的内容

使用 CacheBuilder 构建的缓存不会“自动”执行清理和逐出值,也不会在值过期后立即执行或任何类似操作。相反,它在写入操作期间执行少量维护,或者如果写入很少,则在偶尔的读取操作期间执行。

那么它是如何expireAfterWrite工作的呢?它是否像调度程序一样工作,每 1 分钟运行一次并删除其中的所有条目clientIdMetricCounterCache,然后它会在 1 分钟后再次唤醒并从同一缓存中删除所有条目并继续这样?阅读 wiki 后,我怀​​疑它是否可以这样工作。如果没有,那么我如何可靠地每 1 分钟删除这些记录并发送到其他系统,因为我的写入可能在一段时间内很少见?

看起来我可能必须使用Guava TimeLimiter接口,SimpleTimeLimiter或者可能要ScheduledExecutorService可靠地使呼叫超时,然后删除条目?如果是,任何人都可以提供一个示例,这将如何在我当前的示例中起作用?

4

1 回答 1

2

对我来说,您似乎在滥用缓存,而 Map 会这样做。你没有使用过期,没有大小限制,没有缓存,你只是在收集统计数据。

关于您正在使用的唯一功能是加载方面,这并不值得。

我建议AtomicReference<ConcurrentHashMap<String, AtomicLongMap>>改用:

  • 更新时,您可以通过 获取当前分钟的版本AtomicReference::get
  • 使用,您在您的中clientId查找 an并在未找到时创建一个新的(在 Java 7 或Java 8 上使用)。AtomicLongMapConcurrentHashMapputIfAbsentcomputeIfAbsent
  • 使用name,您更新AtomicLongMap就像您发布的一样。
  • 每分钟一次,您通过AtomicReference::getAndSet.

通过替换,您可以确保您的统计信息不会干扰,但是,您应该稍等片刻,getAndSet因为可能有线程刚刚获得引用并即将写入。

它会比原来的方法产生更多的垃圾,但是所有的垃圾都是短暂的,所以你实际上可能会让 GC 更开心。

它很简单,不需要深入了解库或其实现细节。


我想,volatile而不是AtomicReference也会这样做。

于 2016-12-15T02:42:24.190 回答