千分尺 - Prometheus Gauge 显示 NaN
Posted
技术标签:
【中文标题】千分尺 - Prometheus Gauge 显示 NaN【英文标题】:Micrometer - Prometheus Gauge displays NaN 【发布时间】:2018-11-22 03:10:20 【问题描述】:我正在尝试使用 Micrometer.io 和 Spring Boot 2.0.0.RELEASE 生成 Prometheus 指标。
当我尝试将 List 的大小显示为 Gauge 时,它一直显示 NaN。在文档中是这样说的;
您有责任对您正在使用 Gauge 测量的状态对象进行强引用。
我尝试了一些不同的方法,但我无法解决问题。这是我的一些试验代码。
import io.micrometer.core.instrument.*;
import io.swagger.backend.model.Product;
import io.swagger.backend.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@RestController
@RequestMapping("metrics")
public class ExampleController
private AtomicInteger atomicInteger = new AtomicInteger();
private ProductService productService;
private final Gauge productGauge;
@Autowired
public HelloController(ProductService productService,
MeterRegistry registry)
this.productService = productService;
createGauge("product_gauge", productService.getProducts(), registry);
private void createGauge(String metricName, List<Product> products,
MeterRegistry registry)
List<Product> products = productService.getProducts();
// #1
// this displays product_gauge as NaN
AtomicInteger n = registry.gauge("product_gauge", new AtomicInteger(0));
n.set(1);
n.set(2);
// #2
// this also displays product_gauge as NaN
Gauge
.builder("product_gauge", products, List::size)
.register(registry);
// #3
// this displays also NaN
testListReference = Arrays.asList(1, 2);
Gauge
.builder("random_gauge", testListReference, List::size)
.register(registry);
// #4
// this also displays NaN
AtomicInteger currentHttpRequests = registry.gauge("current.http.requests", new AtomicInteger(0));
@GetMapping(path = "/product/decrement")
public Counter decrementAndGetProductCounter()
// decrement the gague by one
有没有人可以帮助解决这个问题?任何帮助将不胜感激。
【问题讨论】:
【参考方案1】:在所有情况下,您都必须持有对观察到的实例的强引用。当您的 createGauge()
方法退出时,所有函数堆栈分配的引用都符合垃圾回收条件。
对于#1
,传递您的atomicInteger
字段,如下所示:registry.gauge("my_ai", atomicInteger);
。然后根据需要增加/减少。当千分尺需要查询时,只要找到参考就查询。
对于#2
,传递您的productService
字段和一个lambda。基本上每当查询仪表时,它都会使用提供的对象调用该 lambda:registry.gauge("product_gauge", productService, productService -> productService.getProducts().size());
(不保证语法错误。)
【讨论】:
我怀疑第二种解决方案是否可行。传递给 guage 方法的 productService 仍将使用 WeekReference 引用,因此将被垃圾收集。这意味着当 lambda 将被调用并且需要传递参数时,它将为空。这可能会导致 lambda 执行失败,而不是报告 NaN。 当我们谈论 Spring 托管的 Bean 时,只要上下文存在,它就会工作。 我的错误是你的示例代码是正确的。在这种情况下,WeekReference 不会成为问题,因为它指向一个不会被垃圾回收的 bean。我只想指出它工作的原因是它正在使用一个由于来自 applicationContext 的硬引用而将保留在内存中的对象。因此不会被垃圾收集。以下可能不起作用: ProductService productService = ... // 在本地上下文中定义。 registry.gauge("product_gauge", productService, productService -> productService.getProducts().size());【参考方案2】:我无法使用@panser 解决方案,因为我使用的是带有标签的仪表。我的解决方案涉及使用io.micrometer.core.instrument.Tag
的键和值作为映射键创建com.google.common.util.concurrent.AtomicDouble
缓存,如下:
private static final Map<String, AtomicDouble> GAUGE_CACHE = new HashMap<>();
public void handleGauge(String name, List<Tag> tags, double value)
String gaugeKey = this.gaugeKey(name, tags);
if (!GAUGE_CACHE.containsKey(gaugeKey))
GAUGE_CACHE.put(gaugeKey, new AtomicDouble());
Objects.requireNonNull(this.registry.gauge(name, tags, GAUGE_CACHE.get(gaugeKey))).set(value);
private String gaugeKey(String name, List<Tag> tags)
return name + ":" + tags.stream().map(tag -> tag.getKey() + tag.getValue()).collect(Collectors.joining(""));
这对我的需求非常有效,希望对其他人有所帮助。
【讨论】:
【参考方案3】:当我使用您的方法 #1 meterRegistry.gauge("myGauge", new AtomicDouble())
时,我在 Micrometer.io 仪表上遇到了同样的问题。顺便说一句,我正在使用 Scala。我注意到在我创建了大约 50 个仪表后,之后的新仪表显示NaN
。
我使用的是:
val atomicDouble = new AtomicDouble()
Gauge
.builder("myGauge", atomicDouble, new AtomicDoubleToDoubleFunction)
.strongReference(true)
.register(meterRegistry)
与
class AtomicDoubleToDoubleFunction extends ToDoubleFunction[AtomicDouble]
override def applyAsDouble(value: AtomicDouble): Double = value.doubleValue()
这解决了NaN
问题,我的所有仪表都正确显示。我从 https://www.codota.com/code/java/classes/io.micrometer.core.instrument.Gauge 找到了 .strongReference(true)
示例。
【讨论】:
无论哪种方式,您仍然需要保留对您创建的 AtomicDouble 的引用,在这方面使用构建器并没有太大变化。如果您尝试使用更新的仪表值调用构建器两次,它将什么也不做(仪表将永远保持以前的值)。【参考方案4】:我的仪表使用示例
private final AtomicLong countTryUsers = new AtomicLong(0);
Metrics.gauge("app.countTry", countTryUsers);
public void updateCountTryUsers(Long countTryUsersDb)
countTryUsers.set(countTryUsersDb);
所以我只注册一次 app.countTry,然后通过自定义方法 updateCountTryUsers() 更新 AtomicLong countTryUsers
【讨论】:
以上是关于千分尺 - Prometheus Gauge 显示 NaN的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Java prometheus sdk 创建带有标签的 Gauge 度量?
Spring-Boot-Actuator Endpoint Prometheus 未正确显示