第 3 章 Quarkus 应用程序的自定义指标


Micrometer 提供了一个 API,允许您构建自己的自定义指标。监控系统支持的最常见的分段类型包括 Ggauges、计数器和总结。以下小节构建了一个示例端点,并使用这些基本测量类型观察端点行为。

要注册游戏,您需要对 MeterRegistry 的引用,该引用由 Micrometer 扩展进行配置和维护。MeterRegistry 可以注入到应用程序中,如下所示:

package org.acme.micrometer;

import io.micrometer.core.instrument.MeterRegistry;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;

@Path("/")
@Produces("text/plain")
public class ExampleResource {

    private final MeterRegistry registry;

    ExampleResource(MeterRegistry registry) {
        this.registry = registry;
    }
}
Copy to Clipboard Toggle word wrap

Micrometer 具有惯例,例如,必须创建分段,并使用点分隔段,例如 a.name.like. this。然后 Micrometer 将该名称转换为所选 registry 首选格式。Prometheus 使用下划线,这意味着之前的名称在 Prometheus 格式的指标输出中显示为 a_name_like_ this

Micrometer 在唯一指标标识符和标签组合和特定分段实例之间维护内部映射。使用 寄存器计数器 或其他方法递增计数器或记录值不会创建温度的新实例,除非之前没有看到标识符和标签值的组合。

gauges

Guguges 测量一个可随时间增加或减少的值,比如付款的速度。在监控缓存或集合的统计信息时,Guguges 很有用。考虑以下简单示例来观察列表的大小:

    LinkedList<Long> list = new LinkedList<>();

    // Update the constructor to create the gauge
    ExampleResource(MeterRegistry registry) {
        this.registry = registry;
        registry.gaugeCollectionSize("example.list.size", Tags.empty(), list);
    }

    @GET
    @Path("gauge/{number}")
    public Long checkListSize(@PathParam("number") long number) {
        if (number == 2 || number % 2 == 0) {
            // add even numbers to the list
            list.add(number);
        } else {
            // remove items from the list for odd numbers
            try {
                number = list.removeFirst();
            } catch (NoSuchElementException nse) {
                number = 0;
            }
        }
        return number;
    }
Copy to Clipboard Toggle word wrap

使用 Prometheus 时,创建的 gauge 的值并在 Prometheus 端点被访问时观察到列表的大小。务必要注意,gaug 被抽样而不是设置,没有记录与 gauge 关联的值如何在测量之间有所变化。

Micrometer 为创建 gauges 提供了一些额外的机制。请注意,micrometer 不会创建对默认观察到的对象的强引用。根据 registry,micrometer 可以省略 gauges 来观察整个垃圾回收的对象,或使用 NaN (不是数字)作为观察到的值。

永远不会计算您可以计算的任何内容。gauges 可能比计数器更低。如果您要计算量(因为值始终递增),请使用计数器。

计数器

计数器用于测量仅增加的值。在以下示例中,您将计算测试数字的次数,以查看它是主数:

    @GET
    @Path("prime/{number}")
    public String checkIfPrime(@PathParam("number") long number) {
        if (number < 1) {
            return "Only natural numbers can be prime numbers.";
        }
        if (number == 1 || number == 2 || number % 2 == 0) {
            return number + " is not prime.";
        }

        if ( testPrimeNumber(number) ) {
            return number + " is prime.";
        } else {
            return number + " is not prime.";
        }
    }

    protected boolean testPrimeNumber(long number) {
        // Count the number of times we test for a prime number
        registry.counter("example.prime.number").increment();
        for (int i = 3; i < Math.floor(Math.sqrt(number)) + 1; i = i + 2) {
            if (number % i == 0) {
                return false;
            }
        }
        return true;
    }
Copy to Clipboard Toggle word wrap

可能需要尝试向计数器添加标签或标签来指示要检查的值。请记住,指标名称(testPrimeNumber)和标签值的每个唯一组合都会生成唯一的时间序列。将未绑定的数据集用作标签值可能会导致"cardinality explosion",创建新时间序列的指数增加。

但是,可以添加一个标签来传递较少的信息。在以下示例中,进行了调整,计数器被移动以添加一些标签。

    @GET
    @Path("prime/{number}")
    public String checkIfPrime(@PathParam("number") long number) {
        if (number < 1) {
            registry.counter("example.prime.number", "type", "not-natural").increment();
            return "Only natural numbers can be prime numbers.";
        }
        if (number == 1 ) {
            registry.counter("example.prime.number", "type", "one").increment();
            return number + " is not prime.";
        }
        if (number == 2 || number % 2 == 0) {
            registry.counter("example.prime.number", "type", "even").increment();
            return number + " is not prime.";
        }

        if ( testPrimeNumber(number) ) {
            registry.counter("example.prime.number", "type", "prime").increment();
            return number + " is prime.";
        } else {
            registry.counter("example.prime.number", "type", "not-prime").increment();
            return number + " is not prime.";
        }
    }

    protected boolean testPrimeNumber(long number) {
        for (int i = 3; i < Math.floor(Math.sqrt(number)) + 1; i = i + 2) {
            if (number % i == 0) {
                return false;
            }
        }
        return true;
    }
Copy to Clipboard Toggle word wrap

查看此计数器生成的数据,您可以告诉如何检查负数或数字数,或者一个数字数,等等。尝试以下序列,并在纯文本输出中查找 example_prime_number_total。请注意,当 Micrometer 将 Prometheus 命名约定应用到 example.prime.number (最初指定的计数器名称)时,会添加 _total 后缀。

# If you did not leave quarkus running in dev mode, start it again:
./mvnw compile quarkus:dev

curl http://localhost:8080/example/prime/-1
curl http://localhost:8080/example/prime/0
curl http://localhost:8080/example/prime/1
curl http://localhost:8080/example/prime/2
curl http://localhost:8080/example/prime/3
curl http://localhost:8080/example/prime/15
curl http://localhost:8080/q/metrics
Copy to Clipboard Toggle word wrap

永不可以计算您可以花费时间或总结的时间。计数器仅记录一个计数,这可能是需要的。但是,如果您想要了解更多有关值变化的信息,计时器(当测量的基本单位为时间时),或者分布概述可能更为合适。

总结和时间

Micrometer 中的计时器和发行版摘要非常相似。两者都允许您记录观察到的值,该值将与其他记录的值合并,并作为总和保存。Micrometer 还递增计数器,以指示已记录的测量数量,并在指定时间段内跟踪最大观察到的值。

分发摘要通过调用 记录 方法记录观察到的值来填充,而计时器则提供特定于时间和测量持续时间的额外功能。例如,我们可以使用计时器来测量使用打包机制调用 的记录 方法之一计算主数字所需的时间:

    protected boolean testPrimeNumber(long number) {
        Timer timer = registry.timer("example.prime.number.test");
        return timer.record(() -> {
            for (int i = 3; i < Math.floor(Math.sqrt(number)) + 1; i = i + 2) {
                if (number % i == 0) {
                    return false;
                }
            }
            return true;
        });
    }
Copy to Clipboard Toggle word wrap

Micrometer 在为这个计时器发出指标时会应用 Prometheus 约定。Prometheus 测量时间(以秒为单位)。Micrometer 将测量的持续时间转换为秒,并按惯例在指标名称中包含单元。在访问 prime 端点后,查看以下三个条目的纯文本输出: example_prime_number_test_seconds_count,example_prime_number_test_seconds_sum, 和 example_prime_number_test_seconds_max

# If you did not leave quarkus running in dev mode, start it again:
./mvnw compile quarkus:dev

curl http://localhost:8080/example/prime/256
curl http://localhost:8080/q/metrics
curl http://localhost:8080/example/prime/7919
curl http://localhost:8080/q/metrics
Copy to Clipboard Toggle word wrap

计时器和发行版摘要都可以配置为发出其他统计信息,如直方图数据、预计算百分比或服务级别目标(SLO)边界。请注意,计数、总和直方数据可以在维度(或一系列实例内)中重新聚合,而预先计算的值则不能被重新计算。

返回顶部
Red Hat logoGithubredditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。 了解我们当前的更新.

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

Theme

© 2025 Red Hat