[spring-projects/spring-boot]指标端点正在总结相同的指标

2024-04-17 768 views
3

我们使用 Spring Boot 2.0.3 执行器。

我们过去常常MetricAutoConfiguration使用 Micrometer 全局变量CompositeMeterRegistry,并且我们在类路径中都有StatsdMeterRegistry和 。JmxMeterRegistry我们在 Spring Boot 执行器中看到计数错误MetricsEndpoint,其中单个米计数器的值加倍:https://github.com/spring-projects/spring-boot/blob/80da9cf5ebdc256c0b743fd0c34c451a368530aa/spring-boot-project/ spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/MetricsEndpoint.java#L135

例如,我们有一个用于跟踪200HTTP 响应的指标,并且该指标被添加到两个StepMeterRegisty对象中。我们预计会出现这种情况,因为该值已添加到全局中,CompositeMeterRegistry并将发送给每个全局StepMeterRegistry以进行发布。但是MetricsEndpoint,当指标相同时,为什么默认行为是对这些值求和并且基本上将计数指标加倍?

回答

9

@wilkinsona 这是正确的!

3

应用程序拥有多个是一个常见的用例吗StepMeterRegistries?这似乎是问题的根源。

5

例如,CompositeMeterRegistry我们将相同的指标同步到两个不同的测量平台系统(例如 Datadog 和 JMX)中。因此本质上,它们StepMeterRegistry将具有相同的指标和统计数据。

因此,/metric端点正在做的是从CompositeMeterRegistry每个内部注册表中获取所有内部注册表并查询相同的指标名称(此处为 Dtatadog 和 JMX),并从中获取相同的统计信息并对它们进行总结。

按照我们的理解,CompositeMeterRegistry聚合其他计量表注册表并提供单个接口来收集指标。因此,大多数时候我们会在内部注册表中找到相同的指标和统计数据。

https://github.com/spring-projects/spring-boot/blob/80da9cf5ebdc256c0b743fd0c34c451a368530aa/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/metrics/MetricsEndpoint。 java#L112

if (registry instanceof CompositeMeterRegistry) {
            ((CompositeMeterRegistry) registry).getRegistries()
                    .forEach((member) -> collectMeters(meters, member, name, tags));
        }
        else {
            meters.addAll(registry.find(name).tags(tags).meters());
        }
0

谢谢你,@jkschneider。

我想我在这里遗漏了一些东西,因为汇总所有注册表中的值对我来说没有意义。举一个具体的例子,如果我有一个在 JVM 中运行的具有两个计量注册表的应用程序,该 JVM 有 36 个活动线程,则指标端点会告诉我有 72 个活动线程:

$ http :8080/actuator/metrics/jvm.threads.live
HTTP/1.1 200
Content-Disposition: inline;filename=f.txt
Content-Type: application/vnd.spring-boot.actuator.v2+json;charset=UTF-8
Date: Wed, 15 Aug 2018 17:09:16 GMT
Transfer-Encoding: chunked

{
    "availableTags": [],
    "baseUnit": null,
    "description": "The current number of live threads including both daemon and non-daemon threads",
    "measurements": [
        {
            "statistic": "VALUE",
            "value": 72.0
        }
    ],
    "name": "jvm.threads.live"
}

没有迹象表明这是实际活动线程数的倍数。我想在某些情况下它可能不是精确的倍数。例如,如果在查询两个注册表之间启动一个线程。

@jkschneider 你能帮助我们理解为什么求和而不是先胜策略是正确的做法吗?

5

展示此用例的具体示例

SpringBoot应用程序

@SpringBootApplication
@RestController
public class SampleApplication {

    @Autowired
    private MeterRegistry meterRegistry;

    Counter success;

    @PostConstruct
    public void init(){
         success = Counter.builder("Success").register(meterRegistry);
    }

    @RequestMapping("/")
    public String index() {
        success.increment();
        return "success";
    }

    public static void main(String[] args) {
        SpringApplication.run(SampleApplication.class, args);
    }

}

应用程序属性

management.metrics.use-global-registry=true
management.endpoints.web.exposure.include=*
management.endpoints.web.base-path=/actuator
management.endpoint.health.show-details=always
management.server.servlet.context-path=/management

# StatsD Micrometer Config (Datadog)
management.metrics.export.statsd.enabled=true
management.metrics.export.statsd.flavor=datadog
management.metrics.export.statsd.host=localhost
management.metrics.export.statsd.max-packet-length=1400
management.metrics.export.statsd.polling-frequency=PT10S
management.metrics.export.statsd.port=8125
management.metrics.export.statsd.publish-unchanged-meters=true 

# graphite Micrometer Config
management.metrics.export.graphite.duration-units=seconds
management.metrics.export.graphite.enabled=true
management.metrics.export.graphite.host=localhost
management.metrics.export.graphite.port=2004
management.metrics.export.graphite.protocol=pickled
management.metrics.export.graphite.rate-units=seconds
management.metrics.export.graphite.step=1m
management.metrics.export.graphite.tags-as-prefix=test

management.metrics.binders.files.enabled=true
management.metrics.binders.integration.enabled=true
management.metrics.binders.jvm.enabled=true
management.metrics.binders.logback.enabled=true
management.metrics.binders.processor.enabled=true
management.metrics.binders.uptime.enabled=true

pom.xml

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-statsd</artifactId>
            <version>1.0.6</version>
        </dependency>
        <dependency>
            <groupId>io.micrometer</groupId>
            <artifactId>micrometer-registry-graphite</artifactId>
            <version>1.0.6</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

现在,当您到达/端点时,计数器应该增加到 1,并且/actuator/metrics/Success应该将其显示为,1但其显示为

{
    "name": "Success",
    "description": null,
    "baseUnit": null,
    "measurements": [
        {
            "statistic": "COUNT",
            "value": 2
        }
    ],
    "availableTags": []
}
3

抱歉,目的是跨维度求和,而不是跨注册表求和。我想说这是一个错误。

4

@jkschneider @wilkinsona,谢谢您的分析。

我们想借此机会修复这个错误。我们只想讨论此修复的方法。在我看来,先胜策略是正确的做法(请确认)。我认为如果我们更改地图合并功能以忽略第二次更新将修复它(v1, v2) -> v1

另外,这里需要MAX吗?

private BiFunction<Double, Double, Double> mergeFunction(Statistic statistic) {
        return Statistic.MAX.equals(statistic) ? Double::max : Double::sum;
    }
5

@jkschneider 你能看一下上面的评论吗?

4

我认为如果完全消除求和,您也会无意中消除维度聚合。最大值对于维度聚合是必需的。换句话说,我们不应该跨注册表求和/最大化指标,而应该跨维度求和/最大化它们。

5

您好,@jkschneider,谢谢您的回复。

一个快速跟进的问题是,这里的维度聚合是什么意思?一个小例子将帮助我们正确理解逻辑。

换句话说,我们不应该跨注册表求和/最大化指标,而应该跨维度求和/最大化它们。

Dimensions:您的意思是相同的指标名称但不同的标签吗?

1

维度:您的意思是相同的指标名称但不同的标签吗?

是的。

5

感谢@wilkinsona,

这个测试用例很好地解释了这一点。只是为了审查我的想法,假设我们有两个内部寄存器,其中CompositeMeterRegistry包含以下指标

注册表1

名称:: “cache.result.hit”:10 标签:“主机”,“1” 名称:: “cache.result.hit”:12 标签:“主机”,“2” 名称:: “缓存。 result.miss”,:5标签:“host”,“1” 名称:: “cache.result.miss”,:5标签:“host”,“2”

注册表2

name: : "cache.result.hit" value : 10 tag : "host", "1" name: : "cache.result.hit" value : 15 tag : "host", "2" (值不同) name : : “cache.result.miss”,:5标签:“host”,“1” 名称: : “cache.result.miss”,:5标签:“host”,“2”

输出

名称:: “cache.result.hit”:25 标签:[“host”,“1”],[“host”,“2”](Registry2胜利) 名称:: “cache.result.miss”:10 标签:[“主机”,“1”],[“主机”,“2”]

逻辑
  1. 对于 CompositeRegistry 内的每个内部注册表
  2. 跨维度的总和/最大值。
  3. 仅当其他注册表中的尺寸值更大时才更新尺寸值

请确认这个逻辑是否可以实现?

抄送:@jkschneider

0

一切看起来都不错,只是我不会费心去尝试确定哪个注册表具有更大的总和值。每次只需选择组合中的第一个注册表,然后忘记另一个。

请记住,与 Spring Boot 1.x 不同,/actuator/metrics只是一个诊断工具。它的输出不应该用于实际驱动决策。您确实应该依赖于每个注册表如何向其目标后端公开指标。

5

@jkschneider,请查看。

6

结束有利于 PR #14497