问题描述
我正在使用io.prometheus.client.Gauge
实现一个线程安全计数器,以计算在一个时间范围内处理的事件数。现在,有几个线程正在处理事件。完成处理后,所有人都可以更新计数器。我的问题是Gauge
计数器本质上是线程安全的吗?以下是我的实现。
private Map<String,Gauge> gaugeMap = new ConcurrentHashMap<>();
// This method will be called to register the Gauge
private void registerCounter(String metricName) {
Gauge gauge = Gauge.build()
.name(metricName)
.help(metricName)
.register(meterRegistry.getPrometheusRegistry());
gaugeMap.put(metricName,gauge);
}
public void incrementCounter(String metricName) {
if (isCounterPresent(metricName)) {
gaugeMap.get(metricName).inc();
}
}
public void incrementCounter(String metricName,long value) {
if (isCounterPresent(metricName)) {
gaugeMap.get(metricName).inc(value);
}
}
以下是我的客户代码。
// on application startup I am calling registerCounter for all metrics
@postconstruct
private void registerMetrics(List<String> metricList) {
// for each of metricList --> call registerCounter(String metricName)
}
Thread1
-------------------
// process N events
// call incrementCounter("metric-1",N);
Thread2
-------------------
// process N events
// call incrementCounter("metric-1",N);
Thread3
-------------------
// process N events
// call incrementCounter("metric-1",N);
现在我的问题是,因为多个线程正在递增同一个计数器,那么它将给我正确的值吗?
解决方法
Prometheus JVM客户端README指出:
,Gauge上的默认
inc()
,dec()
和set()
方法负责线程安全
压力表是线程安全的,并且使用CAS操作来支持非阻塞状态更新。所有度量标准收集器都是。最后,它们必须由端点在单独的线程中公开。因此,该状态对于读取和写入应保持一致。 要记住的一件事是,争用越高,CAS操作的效率就越低。因此,请确保不要在试图同时更新它的数十个线程中公开它。
第二,使用仪表作为计数器不是一个好主意,因为有一个特殊的类型-计数器。一些Prometheus功能是专门实现和优化的,可用于计数器-速率,速率等。 因此,如果需要收集并公开多个线程处理的事件数,建议使用Counter。