2016-12-13 1 views
0

Guava LoadingCache을 사용하여 데이터를 채우고 1 분 간격으로 LoadingCache에서 모든 항목을 제거하고 싶습니다.Guava 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 분마다 모든 항목을 삭제하고이 캐시에 전혀 읽지 않고 그냥 쓰고 그 다음에 삭제합니다. 다른 시스템으로 보내 레코드. 아래는 내가 "자동"는 구아바에 정리를 수행하고 값을 퇴거하지 않는 CacheBuilder로 구축 wiki

캐시를 보았다 또는 즉시 값이 만료 된 후, 또는 종류의 어떤 것입니다. 대신, 쓰기 조작 중에 또는 8 기 수가 희박한 경우 8 분의 읽기 조작 중에 소량의 유지 보수를 수행합니다.

그럼 expireAfterWrite은 어떻게 작동합니까? 그것은 1 분마다 실행하고 모든 항목을 거기에 뭐든지간에 clientIdMetricCounterCache에 삭제하고 다시 1 분 후 일어나서 동일한 캐시에서 모든 항목을 삭제하고 계속 그런 스케줄러처럼 작동합니까? 위키를 읽은 후, 나는 그것이 위와 같이 작동하는지 의심 스럽다. 만약 그렇지 않다면 어떻게하면 1 분마다 그 레코드를 안전하게 삭제하고 다른 시스템으로 보낼 수 있습니까? 필자의 글은 당분간 희귀 할 수 있습니다.

Guava TimeLimiter 인터페이스와 SimpleTimeLimiter을 사용해야 할 수도 있고, 통화를 안정적으로 시간 초과하고 항목을 삭제하려면 ScheduledExecutorService 일 수 있습니다. 그렇다면, 현재 예제에서 어떻게 작동할까요?

+0

'TimeLimiter'와는 아무런 관계가 없지만, 인용 한 문서가 정기적으로'Cache.cleanUp()'을 호출하는 백그라운드 스레드를 생성합니다. – shmosel

+0

캐시 된 값을 바꾸는 대신 캐시 된 값을 수정하기 때문에 다른 스레드가 정리하려는 'AtomicLongMap'에 대한 참조를 가지고 있으면 제거 수신기에서 동시 업데이트가 손실 될 수 있습니다. – shmosel

+0

@shmosel 그래, 위키에서 그걸 봤어. 내가 가지고있는 혼란은 배경 스레드에서해야 할 일입니다. 정기적으로'clientIdMetricCounterCache.cleanUp()'를 의미하는'Cache.cleanUp()'을 호출하면됩니까? 예제를 제공 할 수 있다면, 이렇게하면 혼란 스러울 것입니다. – john

답변

2

내게는 캐시를 잘못 사용하는 것처럼 보입니다.지도가있는 곳에서 캐시를 잘못 사용하는 것처럼 보입니다. 만료가 없으며 크기 제한이 없습니다. 캐싱이 없습니다, 통계 만 수집 중입니다.

당신이 사용하고있는 유일한 기능은 로딩 기능입니다. 그만한 가치는 없습니다.

내가 대신 AtomicReference<ConcurrentHashMap<String, AtomicLongMap>> 사용하는 것이 좋을 것 :

  • 업데이트, 당신은 AtomicReference::get를 통해 현재의 분의 버전을 얻는다.
  • clientId을 사용하면 ConcurrentHashMap에서 AtomicLongMap을 찾아서 찾지 못한 경우 새 번호를 만듭니다 (Java 7에서는 putIfAbsent, Java 8에서는 computeIfAbsent을 사용하십시오).
  • name을 사용하면 게시 한 것처럼 AtomicLongMap을 업데이트합니다.
  • 분당 한 번씩 AtomicReference::getAndSet을 통해 모든 것을 바꿉니다.교체로

, 당신은 단지 참조를 취득 및 쓰기하려고 스레드가있을 수 있습니다 그러나, 당신은 getAndSet 후 조금 기다려야합니다, 당신의 통계를 방해하지 않도록 할 수 있습니다.

원래 접근법보다 더 많은 쓰레기를 생성하지만 모든 쓰레기는 짧은 생활을 할 것이므로 실제로 GC를 더 행복하게 만들 수 있습니다.

간단하고 라이브러리 또는 구현 세부 정보에 대한 깊은 지식이 필요하지 않습니다.


내 생각은, volatile 대신 AtomicReference의도 할 것입니다.

+0

여기서'AtomicReference' 또는'volatile'을 사용해야합니까? 방금 이걸 가지고 여러 스레드에서이 맵에 채워 넣으면 문제가 발생합니까? 'ConcurrentMap > clientIdMetricCounterCache =지도 .newConcurrentMap();' – john

+0

@ david 내 아이디어는 분당 한 번씩 새로운지도로 대체하는 것이 었습니다. 이 아이디어는 캐시 대신 맵을 사용하는 부분과 직각을 이룹니다. 맵을 교체하지 않고도 작업을 수행 할 수 있습니다. 일반 작업이 추가되고 사용되는 동안 엔트리를 제거해야 할 수도 있기 때문에 정기적 인 작업이 더 복잡해 질 수 있습니다. – maaartinus

관련 문제