java技术指标_使用 Micrometer 记录 Java 应用性能指标

使用 Micrometer 记录 Java 应用性能指标

2771ca892c7b2591131a36eb05b9a9bb.png

成 富

2019 年 2 月 13 日发布

运行良好的应用离不开对性能指标的收集。这些性能指标可以有效地对生产系统的各方面行为进行监控,帮助运维人员掌握系统运行状态和查找问题原因。性能指标监控通常由两个部分组成:第一个部分是性能指标数据的收集,需要在应用程序代码中添加相应的代码来完成;另一个部分是后台监控系统,负责对数据进行聚合计算和提供

API 接口。在应用中使用计数器、计量仪和计时器来记录关键的性能指标。在专用的监控系统中对性能指标进行汇总,并生成相应的图表来进行可视化分析。

目前已经有非常多的监控系统,常用的如 Prometheus、New Relic、Influx、Graphite 和

Datadog,每个系统都有自己独特的数据收集方式。这些监控系统有的是需要自主安装的软件,有的则是云服务。它们的后台实现千差万别,数据接口也是各有不同。在指标数据收集方面,大多数时候都是使用与后台监控系统对应的客户端程序。此外,这些监控系统一般都会提供不同语言和平台使用的第三方库,这不可避免的会带来供应商锁定的问题。一旦针对某监控系统的数据收集代码添加到应用程序中,当需要切换监控系统时,也要对应用程序进行大量的修改。Micrometer

的出现恰好解决了这个问题,其作用可以类比于 SLF4J 在 Java 日志记录中的作用。

Micrometer 简介

Micrometer 为 Java 平台上的性能数据收集提供了一个通用的 API,应用程序只需要使用 Micrometer 的通用 API 来收集性能指标即可。Micrometer

会负责完成与不同监控系统的适配工作。这就使得切换监控系统变得很容易。Micrometer 还支持推送数据到多个不同的监控系统。

在 Java 应用中使用 Micrometer 非常的简单。只需要在 Maven 或 Gradle 项目中添加相应的依赖即可。Micrometer 包含如下三种模块,分组名称都是

io.micrometer:包含数据收集 SPI 和基于内存的实现的核心模块 micrometer-core。

针对不同监控系统的实现模块,如针对 Prometheus 的 micrometer-registry-prometheus。

与测试相关的模块 micrometer-test。

在 Java 应用中,只需要根据所使用的监控系统,添加所对应的模块即可。比如,使用 Prometheus 的应用只需要添加

micrometer-registry-prometheus 模块即可。模块 micrometer-core 会作为传递依赖自动添加。本文使用的 Micrometer 版本是

1.1.1。清单 1 给出了使用 Micrometer 的 Maven 项目的示例:

清单 1. 使用 Micrometer 的 Maven

项目

io.micrometer

micrometer-registry-prometheus

1.1.1

计量器注册表

Micrometer

中有两个最核心的概念,分别是计量器(Meter)和计量器注册表(MeterRegistry)。计量器表示的是需要收集的性能指标数据,而计量器注册表负责创建和维护计量器。每个监控系统有自己独有的计量器注册表实现。模块

micrometer-core 中提供的类 SimpleMeterRegistry 是一个基于内存的计量器注册表实现。SimpleMeterRegistry

不支持导出数据到监控系统,主要用来进行本地开发和测试。

Micrometer 支持多个不同的监控系统。通过计量器注册表实现类 CompositeMeterRegistry

可以把多个计量器注册表组合起来,从而允许同时发布数据到多个监控系统。对于由这个类创建的计量器,它们所产生的数据会对 CompositeMeterRegistry

中包含的所有计量器注册表都产生影响。在清单 2 中,我创建了一个 CompositeMeterRegistry 对象,并在其中添加了两个 SimpleMeterRegistry

对象。一个 SimpleMeterRegistry 对象在创建时通过实现 SimpleConfig 接口提供了不同的名称前缀。

清单 2. CompositeMeterRegistry

使用示例public class CompositeMeterRegistryExample {

public static void main(String[] args) {

CompositeMeterRegistry registry = new CompositeMeterRegistry();

registry.add(new SimpleMeterRegistry());

registry.add(new SimpleMeterRegistry(new MyConfig(), Clock.SYSTEM));

Counter counter = registry.counter("simple");

counter.increment();

}

private static class MyConfig implements SimpleConfig {

public String get(final String key) {

return null;

}

public String prefix() {

return "my";

}

}

}

Micrometer 本身提供了一个静态的全局计量器注册表对象 Metrics.globalRegistry。该注册表是一个组合注册表。使用 Metrics

类中的静态方法创建的计量器,都会被添加到该全局注册表中。对于大多数应用来说,这个全局注册表对象就可以满足需求,不需要额外创建新的注册表对象。不过由于该对象是静态的,在某些场合,尤其是进行单元测试时,会产生一些问题。在

清单 3 中,Metrics.addRegistry() 方法直接在全局注册表对象中添加新的注册表对象,而 Metrics.counter()

方法创建的计数器自动添加到全局注册表中。

清单 3.

使用全局计量器注册表对象public class GlobalRegistryExample {

public static void main(String[] args) {

Metrics.addRegistry(new SimpleMeterRegistry());

Counter counter = Metrics.counter("simple");

counter.increment();

}

}

使用计量器

计量器用来收集不同类型的性能指标信息。Micrometer 提供了不同类型的计量器实现。计量器对象由计量器注册表创建并管理。

计量器名称和标签

每个计量器都有自己的名称。由于不同的监控系统有自己独有的推荐命名规则,Micrometer 使用句点 . 分隔计量器名称中的不同部分,如 a.b.c。Micrometer

会负责完成所需的转换,以满足不同监控系统的需求。

每个计量器在创建时都可以指定一系列标签。标签以名值对的形式出现。监控系统使用标签对数据进行过滤。除了每个计量器独有的标签之外,每个计量器注册表还可以添加通用标签。所有该注册表导出的数据都会带上这些通用标签。

在清单 4 中,使用 MeterRegistry 的 config() 方法可以得到该注册表对象的 MeterRegistry.Config 对象,再使用 commonTags()

方法来设置通用标签。多个标签按照名称和值依次排列的方式来指定。在创建计量器时,在提供了名称之后,以同样的方式指定该计量器的标签。

清单 4.

计量器注册表的通用标签SimpleMeterRegistry registry = new SimpleMeterRegistry();

registry.config().commonTags("tag1", "a", "tag2", "b");

Counter counter = registry.counter("simple", "tag3", "c");

counter.increment();

计数器

计数器(Counter)表示的是单个的只允许增加的值。通过 MeterRegistry 的 counter() 方法来创建表示计数器的 Counter 对象。还可以使用

Counter.builder() 方法来创建 Counter 对象的构建器。Counter 所表示的计数值是 double 类型,其 increment()

方法可以指定增加的值。默认情况下增加的值是 1.0。

如果已经有一个方法返回计数值,可以直接从该方法中创建类型为 FunctionCounter 的计数器。在清单 5 中,方法 counter() 使用了两种不同的方法来创建

Counter 对象。方法 functionCounter() 同样使用了两种不同的方法来创建 FunctionCounter 对象。

清单 5.

计数器使用示例public class Counters {

private SimpleMeterRegistry registry = new SimpleMeterRegistry();

private double value = 0.0;

public void counter() {

Counter counter1 = registry.counter("simple1");

counter1.increment(2.0);

Counter counter2 = Counter.builder("simple2")

.description("A simple counter")

.tag("tag1", "a")

.register(registry);

counter2.increment();

}

public void functionCounter() {

List tags = new ArrayList<>();

registry.more().counter("function1", tags, this, Counters::getValue);

FunctionCounter functionCounter = FunctionCounter.builder("function2", this, Counters::getValue)

.description("A function counter")

.tags(tags)

.register(registry);

functionCounter.count();

}

private double getValue() {

return value++;

}

}

计量仪

计量仪(Gauge)表示的是单个的变化的值。与计数器的不同之处在于,计量仪的值并不总是增加的。与创建 Counter 对象类似,Gauge 对象可以从计量器注册表中创建,也可以使用

Gauge.builder() 方法返回的构造器来创建。清单 6 中给出了计量仪的使用示例,其中 gauge() 方法创建的是记录任意 Number

对象的值,gaugeCollectionSize() 方法记录集合的大小,gaugeMapSize() 方法记录 Map 的大小。需要注意的是,这 3 个方法返回的并不是 Gauge

对象,而是被记录的对象。这是由于 Gauge 对象一旦被创建,就不能手动对其中的值进行修改。在每次取样时,Gauge 会返回当前值。正因为如此,得到一个 Gauge

对象,除了进行测试之外,没有其他的意义。

清单 6.

计量仪使用示例public class Gauges {

private SimpleMeterRegistry registry = new SimpleMeterRegistry();

public void gauge() {

AtomicInteger value = registry.gauge("gauge1", new AtomicInteger(0));

value.set(1);

List list = registry.gaugeCollectionSize("list.size", Collections.emptyList(), new ArrayList<>());

list.add("a");

Map map = registry.gaugeMapSize("map.size", Collections.emptyList(), new HashMap<>());

map.put("a", "b");

Gauge.builder("value", this, Gauges::getValue)

.description("a simple gauge")

.tag("tag1", "a")

.register(registry);

}

private double getValue() {

return ThreadLocalRandom.current().nextDouble();

}

}

计时器

计时器(Timer)通常用来记录事件的持续时间。计时器会记录两类数据:事件的数量和总的持续时间。在使用计时器之后,就不再需要单独创建一个计数器。计时器可以从注册表中创建,或者使用

Timer.builder() 方法返回的构建器来创建。Timer 提供了不同的方式来记录持续时间。第一种方式是使用 record() 方法来记录 Runnable 和

Callable 对象的运行时间;第二种方式是使用 Timer.Sample 来保存计时状态。

在清单 7 中,方法 record() 使用 Timer 对象的 record() 方法来记录一个 Runnable 对象的运行时间。方法 sample() 中首先使用

Timer.start() 来创建一个新的 Timer.Sample 对象并启动计时。调用 Timer.Sample 的 stop() 方法把记录的时间保存到 Timer

对象中。

清单 7.

计时器使用示例public class Timers {

private SimpleMeterRegistry registry = new SimpleMeterRegistry();

public void record() {

Timer timer = registry.timer("simple");

timer.record(() -> {

try {

Thread.sleep(3000);

} catch (InterruptedException e) {

e.printStackTrace();

}

});

}

public void sample() {

Timer.Sample sample = Timer.start();

new Thread(() -> {

try {

Thread.sleep(2000);

} catch (InterruptedException e) {

e.printStackTrace();

}

sample.stop(registry.timer("sample"));

}).start();

}

}

如果一个任务的耗时很长,直接使用 Timer 并不是一个好的选择,因为 Timer 只有在任务完成之后才会记录时间。更好的选择是使用

LongTaskTimer。LongTaskTimer 可以在任务进行中记录已经耗费的时间,它通过注册表的 more().longTaskTimer() 来创建,如清单 8

所示:

清单 8. LongTaskTimer

使用示例public void longTask() {

LongTaskTimer timer = registry.more().longTaskTimer("long");

timer.record(() -> {

try {

Thread.sleep(3000);

} catch (InterruptedException e) {

e.printStackTrace();

}

});

}

分布概要

分布概要(Distribution summary)用来记录事件的分布情况。计时器本质上也是一种分布概要。表示分布概要的类 DistributionSummary

可以从注册表中创建,也可以使用 DistributionSummary.builder()

提供的构建器来创建。分布概要根据每个事件所对应的值,把事件分配到对应的桶(bucket)中。Micrometer 默认的桶的值从 1 到最大的 long 值。可以通过

minimumExpectedValue 和 maximumExpectedValue 来控制值的范围。如果事件所对应的值较小,可以通过 scale

来设置一个值来对数值进行放大。与分布概要密切相关的是直方图和百分比(percentile)。大多数时候,我们并不关注具体的数值,而是数值的分布区间。比如在查看 HTTP

服务响应时间的性能指标时,通常关注是的几个重要的百分比,如 50%,75%和 90%等。所关注的是对于这些百分比数量的请求都在多少时间内完成。Micrometer

提供了两种不同的方式来处理百分比。对于 Prometheus 这样本身提供了对百分比支持的监控系统,Micrometer 直接发送收集的直方图数据,由监控系统完成计算。

对于其他不支持百分比的系统,Micrometer 会进行计算,并把百分比结果发送到监控系统。

在清单 9 中,创建的 DistributionSummary 所发布的百分比包括 0.5、0.75 和 0.9。使用 record() 方法来记录数值,而

takeSnapshot() 方法返回当前数据的快照。

清单 9.

分布概要使用示例public class DistributionSummaries {

private SimpleMeterRegistry registry = new SimpleMeterRegistry();

public void summary() {

DistributionSummary summary = DistributionSummary.builder("simple")

.description("simple distribution summary")

.minimumExpectedValue(1L)

.maximumExpectedValue(10L)

.publishPercentiles(0.5, 0.75, 0.9)

.register(registry);

summary.record(1);

summary.record(1.3);

summary.record(2.4);

summary.record(3.5);

summary.record(4.1);

System.out.println(summary.takeSnapshot());

}

public static void main(String[] args) {

new DistributionSummaries().summary();

}

}

集成监控系统

Micrometer 提供了对多种不同的监控系统的支持。

JMX

JMX 是导出 Micrometer 收集的性能数据的最简单有效的方式。虽然 JMX 所提供的功能比较弱,但是在很多情况下,JMX 就已经可以满足需求了。如果需要导出数据到

JMX,只需要添加对库 io.micrometer:micrometer-registry-jmx 的依赖即可。Micrometer 会根据计量器的名称和标签来生成对应的 JMX

对象名称。默认的命名规则是在计量器名称之后,加上按标签名称字母排序的以句点分隔的名值对。

Prometheus

Prometheus 与其他监控系统的不同在于,Prometheus 采取的是主动抽取数据的方式。因此客户端需要暴露 HTTP 服务,并由 Prometheus

定期来访问以获取数据。Micrometer 的 Prometheus 注册表已经提供了 HTTP 服务所需要返回的内容,只需要使用 Servlet 来提供 HTTP 服务即可。

集成

Spring Boot

从 Spring Boot 2.0 开始,Micrometer 就是 Spring Boot 默认提供的性能指标收集库。Spring Boot Actuator 提供了对

Micrometer 的自动配置。Spring Boot 会自动配置一个组合注册表对象,并把 CLASSPATH 上找到的所有支持的注册表实现都添加起来。只需要在 CLASSPATH

上添加相应的依赖库,Spring Boot 会完成所需的配置。这些注册表对象也会被自动添加到全局注册表对象中。如果需要对该注册表进行配置,添加类型为

MeterRegistryCustomizer 的 bean 即可。在需要使用注册表的地方,可以通过依赖注入的方式来使用

MeterRegistry 对象。

在清单 10 中,Spring 配置类 AppConfig 中声明了一个类型为

MeterRegistryCustomizer 的 bean,可以对

MeterRegistry 进行配置。这里使用 commonTags() 方法来添加通用标签。

清单 10. Spring Boot 中 Micrometer

的配置示例@Configuration

@EnableWebMvc

@ComponentScan(basePackageClasses = AppConfig.class)

class AppConfig {

@Bean

MeterRegistryCustomizer meterRegistryCustomizer() {

return registry -> registry.config().commonTags("tag1", "a", "tag2", "b");

}

}

清单 11 中的 REST 控制器 AppController 通过依赖注入获取到 MeterRegistry 对象,并创建一个计数器。在方法

greeting() 中,对计数器进行递增。

清单 11. 使用 MeterRegistry

对象的示例@RestController

@RequestMapping("/app")

public class AppController {

private final Counter counter;

public AppController(final MeterRegistry registry) {

this.counter = registry.counter("greeting");

}

@RequestMapping("/greeting")

public String greeting() {

this.counter.increment();

return "hello world #" + this.counter.count();

}

}

对于 Prometheus 来说,Spring Boot Actuator 会自动配置一个 URL 为 /actuator/Prometheus 的

HTTP 服务来供 Prometheus 抓取数据。不过该 Actuator 服务默认是关闭的,需要通过 Spring Boot 的配置打开。清单 12 中的

application.yml 文件给出了如何打开该服务的示例。

清单 12. 启用 Prometheus 服务端点的

application.yml

文件management:

endpoints:

web:

exposure:

include: "*"

结束语

作为良好 Java 应用中的重要一环,性能指标数据的收集已经是应用中不可或缺的部分。正如 SLF4J 在 Java 日志记录中的作用一样,Micrometer 为 Java

平台上的性能指标数据收集提供了一个通用的可依赖的 API,避免了可能的供应商锁定问题。利用 Micrometer

提供的多种计量器,可以收集多种类型的性能指标数据,并通过计量器注册表发送到不同的监控系统。

参考资源

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
注:下文中的 *** 代表文件名中的组件名称。 # 包含: 中文-英文对照文档:【***-javadoc-API文档-中文(简体)-英语-对照版.zip】 jar包下载地址:【***.jar下载地址(官方地址+国内镜像地址).txt】 Maven依赖:【***.jar Maven依赖信息(可用于项目pom.xml).txt】 Gradle依赖:【***.jar Gradle依赖信息(可用于项目build.gradle).txt】 源代码下载地址:【***-sources.jar下载地址(官方地址+国内镜像地址).txt】 # 本文件关键字: 中文-英文对照文档,中英对照文档,java,jar包,Maven,第三方jar包,组件,开源组件,第三方组件,Gradle,中文API文档,手册,开发手册,使用手册,参考手册 # 使用方法: 解压 【***.jar中文文档.zip】,再解压其中的 【***-javadoc-API文档-中文(简体)版.zip】,双击 【index.html】 文件,即可用浏览器打开、进行查看。 # 特殊说明: &middot;本文档为人性化翻译,精心制作,请放心使用。 &middot;本文档为双语同时展示,一行原文、一行译文,可逐行对照,避免了原文/译文来回切换的麻烦; &middot;有原文可参照,不再担心翻译偏差误导; &middot;边学技术、边学英语。 &middot;只翻译了该翻译的内容,如:注释、说明、描述、用法讲解 等; &middot;不该翻译的内容保持原样,如:类名、方法名、包名、类型、关键字、代码 等。 # 温馨提示: (1)为了防止解压后路径太长导致浏览器无法打开,推荐在解压时选择“解压到当前文件夹”(放心,自带文件夹,文件不会散落一地); (2)有时,一套Java组件会有多个jar,所以在下载前,请仔细阅读本篇描述,以确保这就是你需要的文件;
### 回答1: 是的,我知道Java的监控指标采集包micrometerMicrometer是一个开源的度量指标库,它支持在JVM应用程序中采集和公开各种类型的指标,例如计数器、分布式直方图和计时器等。Micrometer提供了一个通用的API,可用于在各种监控系统中发送指标,包括Prometheus、InfluxDB、Graphite等。 ### 回答2: 是的,我知道Java的监控指标采集包micrometermicrometer是一个开源的Java监控指标采集库,用于收集和导出应用程序的指标数据。它可以与各种监控系统和组件集成,如Prometheus、Graphite、InfluxDB等,以便对应用程序的运行状况进行监控和分析。 micrometer提供了一组简单而强大的API,使开发人员可以轻松地定义和维护指标。它支持各种指标类型,如计数器、计时器、直方图和分布式汇总。开发人员可以使用这些指标来记录应用程序的性能、响应时间、错误率等关键指标。 使用micrometer,开发人员可以通过编写简单的代码来收集指标数据。他们可以选择将这些数据导出到不同的监控系统,以便进行实时监控和报警。此外,micrometer还提供了丰富的度量工具和可视化组件,帮助开发人员更好地理解和分析指标数据。 总之,micrometer是一个强大且易于使用Java监控指标采集包,它提供了丰富的功能和灵活性,使开发人员能够更好地监控和管理他们的应用程序。无论是小型项目还是大型企业级应用程序,micrometer都是一个不错的选择。 ### 回答3: 是的,我知道Java的监控指标采集包micrometerMicrometer是一个开源的度量工具库,专门用于收集和报告应用程序的指标数据。它为Java应用程序提供了一组简单而强大的API,可以轻松地将关键指标信息导出到各种监控系统中。 Micrometer的主要目标是提供一种统一的编程模型,可以轻松地将指标数据发送到各种不同的监控系统和策略中。它支持多种监控系统,如Prometheus、Graphite、InfluxDB和New Relic等。使用Micrometer,开发人员可以方便地收集应用程序中的关键性能指标,包括响应时间、错误率、请求量等,并将其实时报告给监控系统进行分析和可视化。 Micrometer还支持度量标准的细粒度定义,可以根据应用程序的需求定义自定义指标、标签和计量单位。它提供了一组简单易用的API,使开发人员可以轻松地在应用程序中添加指标收集的代码。 总的来说,Micrometer是一个功能强大且易于使用Java监控指标采集包,它可以帮助开发人员监控和度量应用程序的性能,并将关键指标数据导出到各种监控系统中进行分析和可视化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值