2016-12-05 2 views
0

아래의 클래스에서 incrementdecrement 메트릭을 사용하여 응용 프로그램 메트릭을 측정하려고합니다.문자열 맵과 다른 맵을 스레드로 안전하게 채우는 방법은 무엇입니까?

public class AppMetrics { 
    private final AtomicLongMap<String> metricCounter = AtomicLongMap.create(); 

    private static class Holder { 
    private static final AppMetrics INSTANCE = new AppMetrics(); 
    } 

    public static AppMetrics getInstance() { 
    return Holder.INSTANCE; 
    } 

    private AppMetrics() {}  

    public void increment(String name) { 
    metricCounter.getAndIncrement(name); 
    } 

    public AtomicLongMap<String> getMetricCounter() { 
    return metricCounter; 
    } 
} 

내가 메트릭 이름을 전달하여 통계를 증가 increment 방법 멀티 스레드 코드에서 클래스 AppMetrics의를 호출하고 있습니다.

문제 설명 :

는 지금은 문자열을 각 clientId에 대한 metricCounter을 갖고 싶어. 즉, 동일한 clientId을 여러 번 가져올 수도 있고 때로는 새로운 clientId이 될 수도 있습니다. 따라서 어쨌든 그 clientId에 대한 metricCounter지도를 추출해야하고 해당 특정지도의 메트릭을 증가시켜야합니다 (어떻게해야할지 모르겠습니다). 그).

염두에 두어야 할 올바른 방법은 스레드로부터 안전해야하며 원자 적 조작을 수행해야한다는 것입니다. 그 대신지도를 만들려고했습니다.

private final Map<String, AtomicLongMap<String>> clientIdMetricCounterHolder = Maps.newConcurrentMap(); 

올바른 방법인가요? 그렇다면 어떻게하면 clientId을 키로 전달하여이지도를 채울 수 있으며 값은 각 측정 항목의 카운터지도가됩니다.

나는 자바 7

+1

사용 computeIfAbsent() : http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentMap.html#computeIfAbsent-K-java.util.function.Function - –

+0

죄송합니다. Java 7입니다. 죄송합니다. 언급하는 것을 잊어 버렸습니다. – john

+0

동시 맵을 사용하는 것이 좋습니다.하지만 업데이트가 원자 적인지 확인하는 것이 더 까다 롭습니다. 특히 자바 8이 아닌 경우 특히 그렇습니다. – shmosel

답변

0

Map<String, Map<String, T>> 그냥 변장에 Map<Pair<String, String>, T>입니다입니다. MultiKey 클래스를 만듭니다

class MultiKey { 
    public String clientId; 
    public String name; 

    // be sure to add hashCode and equals 
} 

그럼 그냥 AtomicLongMap<MultiKey>를 사용합니다.

편집 :

측정의 세트가 잘 정의되어 제공, 하나의 클라이언트에 대한 통계를 보려면이 데이터 구조를 사용하기에 너무 어렵지 않을 것이다 :

Set<String> possibleMetrics = // all the possible values for "name" 

Map<String, Long> getMetricsForClient(String client) { 
    return Maps.asMap(possibleMetrics, m -> metrics.get(new MultiKey(client, m)); 
} 

가 반환지도 할 것이다 생방송보기. 이전 Java 버전을 사용하는 경우에는 좀 더 자세한 정보가 표시 될 수 있지만 여전히 가능합니다.

+0

여기서'client'와'metricsType'은 무엇입니까? 나는 따라 가지 않았다? – john

+0

죄송합니다. 귀하의 예제에서'clientId'와'name'. 나는 그 포스트를 편집 할 것이다. – Sam

+1

이것은 좋은 생각입니다.하지만 OP가 키 세트를 반복하여 메트릭을 수집하고 있다고 생각합니다. 이렇게하면 모든 고객의 측정 항목을 반복하고 특정 고객의 측정 항목을 필터링해야합니다. – shmosel

1

지도를 사용하는 경우 새 AtomicLongMap 인스턴스를 동기화해야합니다. 대신 LoadingCache을 사용하는 것이 좋습니다. 실제 "캐싱"기능을 사용하지 않을 수도 있지만 "로드 중"기능은 AtomicLongMap 인스턴스를 동기화하는 데 매우 유용합니다. 예 :

LoadingCache<String, AtomicLongMap<String>> clientIdMetricCounterCache = 
     CacheBuilder.newBuilder().build(new CacheLoader<String, AtomicLongMap<String>>() { 
      @Override 
      public AtomicLongMap<String> load(String key) throws Exception { 
       return AtomicLongMap.create(); 
      } 
     }); 

이제 클라이언트가 새로운 것인지 아닌지에 대한 걱정없이 모든 클라이언트의 메트릭 개수 업데이트를 안전하게 시작할 수 있습니다. 예 :

clientIdMetricCounterCache.get(clientId).incrementAndGet(metricName); 
+0

나는 본다. 이것은 아주 좋은 일입니다 .. 나는 당신의 제안에 샘플 코드 기반으로 질문을 업데이트했다. 그것이 당신이 옳은 의미입니까? – john

+0

예, 개인 캐시는 공개하지 않지만 당신은'AppMetrics' 인스턴스의 클라이언트가 그것을 수정하는 것을 원하지 않습니다. 대신 캐시 내용을 변경 불가능한 맵보기로 반환 할 수 있습니다. 예 : 'Collections.unmodifiableMap (clientIdMetricCounterCache.asMap())'. 이것은 주어진 클라이언트에 대한 메트릭이 없다면 클라이언트 호출이 추가적인 AtomicLongMap 인스턴스를 생성하는 것을 막을 것입니다. 또한'AppMetrics' API에'LoadingCache'를 드러내지 않을 것입니다. 만약 당신이 멀리 떨어지기를 원한다면'Map' 인터페이스를 고수 할 필요가 있습니다. – mfulton26

+0

죄송합니다. 잠시 떨어져있었습니다. 좋은 지적입니다. 제가 옳은 일을하고 있는지 아닌지에 대한 아이디어 나 생각을 저에게 줄 수 있도록 제가하려고하는 일을 설명해 드리겠습니다. 30 초 동안 모든 측정 항목을 보유 할 메트릭 클래스 (AppMetrics)를 만든 다음 해당 측정 항목을 재설정하려고합니다. 30 초마다 모든 측정 항목이 재설정됩니다. – john