使用Micrometer在Spring Boot应用程序中定义自定义指标
使用 Micrometer 在 Spring Boot 应用程序中定义自定义指标
Spring Boot 2.0 为我们喜爱的 Java 框架带来了大量新功能,其中之一便是将 Micrometer 集成到 Spring Boot Actuator 中。
Micrometer 是一个维度指标(metrics)监控门面库,它帮助开发人员将应用程序指标集成到各种监控系统中,同时使应用程序独立于具体的监控实现。正如项目首页所述,它类似于 SLF4J,但是用于指标(metrics)收集。
Micrometer 概述
在深入探讨如何使用 Micrometer 定义自定义指标之前,我们先来明确一下它的定义。
首先,Micrometer 是一个检测适配工具(Instrumentation Facade)。这意味着使用该库,开发人员可以使用统一的方式将指标数据发送到各种监控系统中。您可能会认为这没什么大不了的,确实如此。然而,市面上有许多用于监控应用程序的解决方案,每种方案满足监控需求的方法各不相同。这些差异可能细微如命名约定,也可能根本性地体现在数据收集方法上。
例如,在我们公司(AutSoft),我们使用 Prometheus 轮询应用程序中的新数据,而不是依赖像 DataDog 那样的推送模型。Micrometer 可以为您屏蔽所有这些差异,让您为所有解决方案使用统一的接口。
其次,Micrometer 遵循维度方法(Dimensional Approach),这意味着您可以使用任意数量的标签(Tags)来标记指标。例如,如果您有一个统计应用程序中 HTTP 请求的指标,可以使用请求命中的 URI 对其进行标记。一旦 Prometheus 收集了这些指标,您不仅可以查看请求总数,还可以向下钻取,检查特定 URI 的请求数。
Micrometer 与 Spring
借助新版本的 Spring Boot Actuator,Spring 团队决定使用 Micrometer 来报告内置指标(这并不奇怪,因为他们本身就是该库的开发维护者)。
要在现有的 Spring Boot 2 应用程序中检查这些指标,您实际上不需要做很多工作。只需导入 Spring Boot Actuator 和 Micrometer 依赖项并进行少量配置即可。
首先,将以下行添加到您 pom.xml 的 dependencies 部分:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-core</artifactId>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-registry-prometheus</artifactId>
</dependency>然后,将以下配置粘贴到 application.properties(或 application.yml)文件中以暴露 Prometheus 抓取端点:
management.endpoints.web.exposure.include=prometheus现在,如果启动应用程序并打开 http://localhost:8080/actuator/prometheus 端点,您将看到 Actuator 已经导出的大量指标。如果您查看前几行,就已经可以看到 Micrometer 和 Prometheus 的维度方法在起作用:
# HELP logback_events_total Number of error level events that made it to the logs
# TYPE logback_events_total counter
logback_events_total{level="warn",} 0.0
logback_events_total{level="debug",} 0.0
logback_events_total{level="error",} 0.0
logback_events_total{level="trace",} 0.0
logback_events_total{level="info",} 7.0指标的名称是 logback_events_total,但有一个标签(或维度)叫做 level,这帮助您进行深入分析,准确检查每个日志记录级别发生了多少事件。
定义您的自定义指标
现在,让我们使用 Micrometer 和 Spring Boot 定义并导出自己的自定义指标。为此,我们将使用最喜欢的示例 BeerService,并将对其进行彻底监控。(您应该始终注意啤酒,不是吗?)
首先,我们需要保留 ApplicationContext 的 MeterRegistry 实例:
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.stereotype.Component;
@Component
public class BeerService {
private MeterRegistry meterRegistry;
public BeerService(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
}
}该 MeterRegistry 负责收集和管理应用程序的 meters(计量器)。
定义计数器(Counter)
指标的最基本类型是计数器。计数器用于报告代表计数的单个数字,且通常只增不减。
在我们的示例中,我们将报告进入 BeerService 的订单数量:
private void initOrderCounters() {
lightOrderCounter = this.meterRegistry.counter("beer.orders", "type", "light"); // 1 - 创建计数器
aleOrderCounter = Counter.builder("beer.orders") // 2 - 使用流式 API 创建计数器
.tag("type", "ale")
.description("The number of orders ever placed for Ale beers")
.register(meterRegistry);
}
void orderBeer(Order order) {
orders.add(order);
if ("light".equals(order.type)) {
lightOrderCounter.increment(1.0); // 3 - 增加计数器
} else if ("ale".equals(order.type)) {
aleOrderCounter.increment();
}
}让我们看看这里发生了什么:
- 我们可以使用
meterRegistry创建计数器。创建的指标将自动注册到注册表。 - 创建指标的更简洁方法是使用流式 Builder API。
- 计数器可以增加 1 或任何正数。
剩下要做的唯一件事就是实际订购一些啤酒。将以下行粘贴到 Application 类中,然后启动应用程序:
@SpringBootApplication
public class MicrometerApplication {
public static void main(String[] args) {
SpringApplication.run(MicrometerApplication.class, args);
}
private BeerService beerService;
public MicrometerApplication(BeerService beerService) {
this.beerService = beerService;
}
@EventListener(ApplicationReadyEvent.class)
public void orderBeers() {
Flux.interval(Duration.ofSeconds(2))
.map(MicrometerApplication::toOrder)
.doOnEach(o -> beerService.orderBeer(o.get()))
.subscribe();
}
private static Order toOrder(Long l) {
double amount = l % 5;
String type = l % 2 == 0 ? "ale" : "light";
return new Order(amount, type);
}
}现在,如果您在浏览器中打开 http://localhost:8080/actuator/prometheus 该 URL,则可以在几行中找到我们的新指标:
# HELP beer_orders_total
# TYPE beer_orders_total counter
beer_orders_total{type="light",} 5.0
beer_orders_total{type="ale",} 6.0恭喜,我们已经使用 Micrometer 定义了第一个计数器指标!
定义仪表(Gauge)
接下来,让我们看一下 Gauges。仪表也代表单个数值,但有一些显著差异。首先,计数器存储单调递增的值,而 Gauge 的值也可以递减。其次,Gauge 的值仅在观察时才会更改,我们不会像在前面的示例中那样手动对其进行递增。相反,如果需要,我们提供了一个获取 Gauge 当前值的函数。此行为还意味着两次观察之间发生的任何事件都将丢失。
通常,建议对具有上限的值使用仪表,不建议对计数器可表示的量度使用仪表。
在我们的示例中,我们将使用仪表监视 order 列表的大小。(即使我们当前的“业务逻辑”并未对此列表实施上限,但我们假设我们可以处理的最大订单数量,除此以外的数量也将更多。)
扩展您 BeerService 的构造函数,如下所示:
public BeerService(MeterRegistry meterRegistry) {
this.meterRegistry = meterRegistry;
initOrderCounters();
Gauge.builder("beer.ordersInQueue", orders, Collection::size)
.description("Number of unserved orders")
.register(meterRegistry);
}同样,如果重新启动应用程序并在浏览器中检查 http://localhost:8080/actuator/prometheus URL,则应找到 beer.ordersInQueue 指标:
# HELP beer_ordersInQueue Number of unserved orders
# TYPE beer_ordersInQueue gauge
beer_ordersInQueue 9.0定义计时器(Timer)
计时器具有两个功能:它测量某些事件(通常是方法执行)的时间,并同时对这些事件进行计数。如果您曾经维护或开发过 Web 应用程序,则很可能希望检查服务器的响应时间。这种用例是计时器最典型的用例。
计时器具有许多有用的功能,但我们只专注于测量给定方法的执行时间。在 Spring 中,配置 Micrometer 提供的 Aspect 后可以使用 micrometer-core 的 @Timed 注解 TimedAspect。将以下代码行放入 Application 类(或任何 @Configuration 类)中:
@Bean
public TimedAspect timedAspect(MeterRegistry registry) {
return new TimedAspect(registry);
}(确保导入 spring-boot-starter-aop Maven 依赖关系。)
然后,在 BeerService 中创建一个方法以服务 orders 列表中的第一顺序:
@Scheduled(fixedRate = 5000)
@Timed(description = "Time spent serving orders")
public void serveFirstOrder() throws InterruptedException {
if (!orders.isEmpty()) {
Order order = orders.remove(0);
Thread.sleep(1000L * order.amount);
}
}(要使 @Scheduled 注解生效,您需要使用 @EnableScheduling 注解应用程序或任何其他 Configuration 类。)
现在,如果启动该应用程序,您将找到以下导出到 Prometheus 的指标:
# HELP method_timed_seconds Time spent serving orders
# TYPE method_timed_seconds summary
method_timed_seconds_count{class="com.demo.micrometer.BeerService",exception="none",method="serveFirstOrder",} 8.0
method_timed_seconds_sum{class="com.demo.micrometer.BeerService",exception="none",method="serveFirstOrder",} 11.041940917
# HELP method_timed_seconds_max Time spent serving orders
# TYPE method_timed_seconds_max gauge
method_timed_seconds_max{class="com.demo.micrometer.BeerService",exception="none",method="serveFirstOrder",} 4.003256321您可以看到我们的应用程序在 11.04 秒内处理了 8 个订单,每个订单最多处理 4 秒。
在事件执行尚未完成之前,计时器通常不会报告测量的时间。如果您有很长的任务要在它们仍在运行时进行测量,请使用如下 @Timed 注解:
@Timed(description = "Time spent serving orders", longTask = true)总结和下一步
在本文中,我们了解了 Micrometer 的基础知识,它与 Spring Boot Actuator 集成在一起,为了使事情变得更加令人兴奋,我们在 Spring Boot 应用程序中定义了自己的指标。
现在,您已经知道了 Micrometer 的基础知识,但仍有很多需要探索的地方。我建议您查看 Micrometer 文档 的 概念页面 和/或在 SpringOne Platform 上观看 Jon Schneider 关于 Micrometer 的 演示。
在本文中,我们没有介绍所定义指标的可视化。为此,我们将 Prometheus 与 Grafana 结合 使用,因为它们非常适合我们的用例。
可以在我们的 GitHub 页面 上找到本指南的源代码。
如果您喜欢这篇文章或有任何疑问,请不要在下面发表评论。
参考资料
- Micrometer:Spring Boot 2 的新应用程序指标收集器
- Spring Boot 参考指南 - 指标
- Micrometer 文档 - 概念
- Micrometer 应用指标介绍 - Jon Schneider
说明:本文基于 Spring Boot 2.x 版本编写。Spring Boot 3.x 在依赖包命名空间(如 Jakarta EE)上有所变化,但 Micrometer 的核心用法与本文描述基本一致。端点路径及配置属性在不同版本间可能存在细微差异,请以官方文档为准。
版权声明:本文为原创文章,版权归 戴老师的博客 所有,转载请联系博主获得授权。
如果对本文有什么问题或疑问都可以在评论区留言,我看到后会尽量解答。