我正在使用下面的课程来衡量我的应用指标,其中包括increment
和decrement
指标。
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
方法,通过传递度量标准名称来增加度量标准。
问题陈述:
现在我希望每个metricCounter
都有一个clientId
字符串。这意味着我们也可以多次获得相同的clientId
,有时它会是新的clientId
,所以不知怎的,我需要为metricCounter
提取clientId
地图并增加该特定地图上的指标(我不知道该怎么做)。
正确的方法是什么,记住它必须是线程安全的,并且必须执行原子操作。我想要制作一张这样的地图:
private final Map<String, AtomicLongMap<String>> clientIdMetricCounterHolder = Maps.newConcurrentMap();
这是正确的方法吗?如果是,那么我如何通过传递clientId
作为关键字来填充此地图,它的值将是每个指标的计数器地图。
我在Java 7上。
答案 0 :(得分:1)
如果您使用地图,则需要同步创建新的AtomicLongMap
实例。我建议改用LoadingCache
。您最终可能不会使用任何实际的“缓存”功能,但“加载”功能非常有用,因为它将同步为您创建AtomicLongMap
个实例。 e.g:
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();
}
});
现在,您可以安全地为任何客户端启动更新度量标准计数,而无需担心客户端是否为新用户。 e.g。
clientIdMetricCounterCache.get(clientId).incrementAndGet(metricName);
答案 1 :(得分: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版本,可能会更冗长,但它仍然可能。