io.prometheus.client.Gauge是否可用于线程安全计数器

问题描述

我正在使用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。